Initial commit

Initial commit til Git.
V2 er deployed
This commit is contained in:
2026-06-13 17:31:50 +02:00
parent 9fcd2b145e
commit 41e23b6184
375 changed files with 15956 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "5.0.9",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
namespace Pos
{
public class LoadConfig
{
private string dir;
public LoadConfig()
{
dir = AppDomain.CurrentDomain.BaseDirectory;
}
public IConfiguration ByEnvironment()
{
//Console.Out.WriteLine($"Json path: {dir}");
#if DEBUG
var config = new ConfigurationBuilder()
.SetBasePath(dir)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true)
.Build();
return config;
#else
var config = new ConfigurationBuilder()
.SetBasePath(dir)
.AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true)
.Build();
return config;
#endif
//Running in a environment that not is supported in this setup
throw new Exception("HostingEnvironment is not supported! This config setup only supports Development or Production");
}
public IConfiguration DebugConfiguration()
{
var config = new ConfigurationBuilder()
.SetBasePath(dir)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true)
.Build();
return config;
}
public IConfiguration ReleaseConfiguration()
{
var config = new ConfigurationBuilder()
.SetBasePath(dir)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true)
.Build();
return config;
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Database.Models
{
[Table("employee")]
public class EmployeeEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public bool IsArchived { get; set; }
public bool IsModified { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Database.Models
{
[Table("payment")]
public class PaymentEntity
{
[Key]
public int Id { get; set; }
public int SaleId { get; set; }
public decimal Amount { get; set;}
public string Type { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Database.Models
{
[Table("product")]
public class ProductEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
public bool IsArchived { get; set; }
public int Index { get; set; }
public bool IsModified { get; set; }
public int ProductGroupId { get; set; }
[ForeignKey(nameof(ProductGroupId))]
public ProductGroupEntity ProductGroup { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Database.Models
{
[Table("productgroup")]
public class ProductGroupEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public bool IsArchived { get; set; }
public int Index { get; set; }
public ICollection<ProductEntity> Products { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Database.Models
{
[Table("sale")]
public class SaleEntity
{
[Key]
public int Id { get; set; }
public DateTime Time { get; set; }
public int EmployeeId { get; set; }
public bool IsModified { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Database.Models
{
[Table("sale_line")]
public class SaleLineEntity
{
[Key]
public int Id { get; set; }
public int SaleId { get; set; }
public string Product { get; set; }
public int Pieces { get; set; }
public decimal Price { get; set; }
public decimal Total { get; set; }
}
}

View File

@@ -0,0 +1,35 @@
using Database.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Pos;
namespace Database
{
public class PosDbContext : DbContext
{
public DbSet<EmployeeEntity> Employee { get; set; }
public DbSet<ProductGroupEntity> ProductGroups { get; set; }
public DbSet<ProductEntity> Products { get; set; }
public DbSet<SaleEntity> Sales { get; set; }
public DbSet<SaleLineEntity> SalesLines { get; set; }
public DbSet<PaymentEntity> Payment { get; set; }
protected override void OnConfiguring(
DbContextOptionsBuilder optionsBuilder)
{
LoadConfig l = new LoadConfig();
IConfiguration config = l.ByEnvironment();
string connectionString = config["MariaSqlServer"].ToString();
optionsBuilder
.UseMySql(connectionString,ServerVersion.AutoDetect(connectionString))
.UseLoggerFactory(LoggerFactory.Create(b => b
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
base.OnConfiguring(optionsBuilder);
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Database.Models;
using Microsoft.Extensions.Configuration;
namespace Database.Repository
{
public class EmployeeRepository : IDisposable
{
public void Add(string name)
{
EmployeeEntity employee = new EmployeeEntity();
employee.Name = name;
employee.IsModified = true;
using PosDbContext context = new PosDbContext();
context.Employee.Add(employee);
context.SaveChanges();
}
public EmployeeEntity Get(int employeeId)
{
using PosDbContext context = new PosDbContext();
EmployeeEntity employeeEntity = context.Employee.First(c => c.Id == employeeId);
return employeeEntity;
}
public List<EmployeeEntity> GetAll()
{
using PosDbContext context = new PosDbContext();
List<EmployeeEntity> staffs = context.Employee.Where(c => c.IsArchived == false).ToList();
return staffs;
}
public void Edit(int id, string name)
{
using PosDbContext context = new PosDbContext();
EmployeeEntity employee = context.Employee.Single(c => c.Id == id);
employee.Name = name;
employee.IsModified = true;
context.SaveChanges();
}
public void Delete(int id)
{
using PosDbContext context = new PosDbContext();
EmployeeEntity employee = context.Employee.Single(c => c.Id == id);
employee.IsArchived = true;
employee.IsModified = true;
context.SaveChanges();
}
public void Dispose()
{
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Database.Models;
using Microsoft.EntityFrameworkCore;
namespace Database.Repository
{
public class ProductGroupRepository
{
public List<ProductGroupEntity> GetAll()
{
using PosDbContext context = new PosDbContext();
List<ProductGroupEntity> productGroups = context.ProductGroups
.Include(c => c.Products)
.Where(c => c.IsArchived == false)
.OrderBy(c => c.Index)
.ToList();
return productGroups;
}
public ProductGroupEntity Get(int id)
{
using PosDbContext context = new PosDbContext();
ProductGroupEntity product = context.ProductGroups.Single(c => c.Id == id);
return product;
}
public void Add(string name)
{
ProductGroupEntity productGroup = new ProductGroupEntity();
productGroup.Name = name;
using PosDbContext context = new PosDbContext();
//Get the highest index
ProductGroupEntity highest = context.ProductGroups.OrderByDescending(c => c.Index).Take(1).FirstOrDefault();
if(highest == null)
{
highest = new ProductGroupEntity();
highest.Index = 0;
}
productGroup.Index = highest.Index + 1;
context.ProductGroups.Add(productGroup);
context.SaveChanges();
}
public void Edit(string name, int id)
{
using PosDbContext context = new PosDbContext();
ProductGroupEntity productGroup = context.ProductGroups.Single(c => c.Id == id);
productGroup.Name = name;
context.SaveChanges();
}
public void Archive(int id)
{
using PosDbContext context = new PosDbContext();
ProductGroupEntity productGroup = context.ProductGroups.Single(c => c.Id == id);
productGroup.IsArchived = true;
context.SaveChanges();
}
public void SetIndex(int id, int index)
{
using PosDbContext context = new PosDbContext();
ProductGroupEntity productGroup = context.ProductGroups.Single(c => c.Id == id);
productGroup.Index = index;
context.SaveChanges();
}
public bool Any()
{
using PosDbContext context = new PosDbContext();
bool any = context.ProductGroups.Any();
return any;
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Collections.Generic;
using System.Linq;
using Database.Models;
namespace Database.Repository
{
public class ProductRepository
{
public List<ProductEntity> GetAll()
{
using PosDbContext context = new PosDbContext();
List<ProductEntity> products = context.Products
.Where(c => c.IsArchived == false)
.OrderBy(c => c.Index)
.ToList();
return products;
}
public List<ProductEntity> GetByProductGroup(int id)
{
using PosDbContext context = new PosDbContext();
List<ProductEntity> products = context.Products
.Where(c => c.ProductGroupId == id)
.Where(c => c.IsArchived == false)
.OrderBy(c => c.Index)
.ToList();
return products;
}
public void SetIndex(int id, int index)
{
using PosDbContext context = new PosDbContext();
ProductEntity product = context.Products.Single(c => c.Id == id);
product.Index = index;
product.IsModified = true;
context.SaveChanges();
}
public ProductEntity GetById(int id)
{
using PosDbContext context = new PosDbContext();
ProductEntity product = context.Products.Single(c => c.Id == id);
return product;
}
public void Update(int id, int productGroupId, string name)
{
using PosDbContext context = new PosDbContext();
ProductEntity dbProduct = context.Products.Single(c => c.Id == id);
dbProduct.ProductGroupId = productGroupId;
dbProduct.Name = name;
dbProduct.IsModified = true;
context.SaveChanges();
}
public void Archive(int id)
{
using PosDbContext context = new PosDbContext();
ProductEntity dbProduct = context.Products.Single(c => c.Id == id);
dbProduct.IsArchived = true;
dbProduct.IsModified = true;
context.SaveChanges();
}
public void Add(string name, int productGroupId)
{
ProductEntity product = new ProductEntity();
product.Name = name;
product.ProductGroupId = productGroupId;
using PosDbContext context = new PosDbContext();
//Get the highest index
ProductEntity highest = context.Products.OrderByDescending(c => c.Index).Take(1).FirstOrDefault();
if (highest == null)
{
highest = new ProductEntity();
highest.Index = 0;
}
product.Index = highest.Index + 1;
product.IsModified = true;
context.Products.Add(product);
context.SaveChanges();
}
}
}

View File

@@ -0,0 +1,83 @@
using Database.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Database.Repository
{
public class SaleRepository
{
public SaleEntity New(int employeeId)
{
using PosDbContext context = new PosDbContext();
SaleEntity saleEntity = new SaleEntity();
saleEntity.Time = DateTime.Now;
saleEntity.EmployeeId = employeeId;
saleEntity.IsModified = true;
context.Sales.Add(saleEntity);
context.SaveChanges();
return saleEntity;
}
public SaleLineEntity SaveSaleLine(SaleLineEntity entity)
{
using PosDbContext context = new PosDbContext();
context.SalesLines.Add(entity);
context.SaveChanges();
return entity;
}
public List<SaleLineEntity> GetSaleLineBySaleId(int saleId)
{
using PosDbContext context = new PosDbContext();
List<SaleLineEntity> saleLineEntities = context.SalesLines.Where(c => c.SaleId == saleId).ToList();
return saleLineEntities;
}
public PaymentEntity SavePayment(PaymentEntity entity)
{
using PosDbContext context = new PosDbContext();
context.Payment.Add(entity);
context.SaveChanges();
return entity;
}
public List<PaymentEntity> GetPaymentBySaleId(int saleId)
{
using PosDbContext context = new PosDbContext();
List<PaymentEntity> paymentEntities = context.Payment.Where(c => c.SaleId == saleId).ToList();
return paymentEntities;
}
public SaleEntity GetLatest()
{
using PosDbContext context = new PosDbContext();
SaleEntity saleEntity = context.Sales.OrderByDescending(c => c.Id).Take(1).First();
return saleEntity;
}
public SaleEntity Get(int id)
{
using PosDbContext context = new PosDbContext();
SaleEntity saleEntity = context.Sales.Single(c => c.Id == id);
return saleEntity;
}
public List<SaleEntity> GetByDateRange(DateTime start, DateTime end)
{
using PosDbContext context = new PosDbContext();
List<SaleEntity> saleEntities = context.Sales.Where(c => c.Time.Date >= start.Date && c.Time.Date <= end.Date).OrderByDescending(c => c.Time).ToList();
return saleEntities;
}
public List<SaleEntity> GetByDateRange(DateTime start)
{
using PosDbContext context = new PosDbContext();
List<SaleEntity> saleEntities = context.Sales.Where(c => c.Time.Date == start.Date.Date).OrderByDescending(c => c.Time).ToList();
return saleEntities;
}
}
}

View File

@@ -0,0 +1,45 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EpsonPrinter.Model;
using EpsonPrinter.Services;
namespace EpsonPrinter.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PosPrinterController : ControllerBase
{
/// <summary>
/// PrintReceipt receipt, so far only for all Epson Thermal Printers
/// </summary>
/// PrintStyle is:
/// None
/// FontB
/// Bold
/// DoubleHeight
/// DoubleWidth
/// Underline
/// There is support for any combination of PrintStyles
/// ----------------------------------------
///
///
/// <param name="receiptModel"></param>
[HttpPost]
[Route("Receipt")]
public void PrintReceipt([FromServices] EpsonPrintService epsonPrint, ReceiptModel receiptModel)
{
epsonPrint.PrintReceipt(receiptModel);
}
[HttpPost]
[Route("SaleOfDay")]
public void PrintSaleOfDay([FromServices] EpsonPrintService epsonPrint, SaleOfDayModel saleOfDayModel)
{
epsonPrint.PrintSaleOfDay(saleOfDayModel);
}
}
}

View File

@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace EpsonPrinter.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
[Route("HelloWorld")]
public string Get()
{
return "HelloWorld";
}
}
}

View File

@@ -0,0 +1 @@
* text=auto

View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: ['lukevp']
#patreon: # Replace with a single Patreon username
#open_collective: # Replace with a single Open Collective username
#ko_fi: # Replace with a single Ko-fi username
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -0,0 +1,7 @@
.vs/
.idea/
**/obj/
**/bin/
push.ps1
/push.txt
/ESCPOS_NET/ESCPOS_NET.csproj.user

View File

@@ -0,0 +1,74 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ESCPOS_NET\ESCPOS_NET.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="abe-lincoln.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\abe-lincoln.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\kitten.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-100.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-200.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-300.bmp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-300.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-300.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-300.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-400.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-500.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo-600.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\pd-logo.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="images\Portal_Companion_Cube.jpg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="kitten.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="pd-logo.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="test-files\output-juliogamasso.bin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,267 @@
using ESCPOS_NET.Emitters;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using Microsoft.Extensions.Logging;
using System.Threading;
namespace ESCPOS_NET.ConsoleTest
{
internal class Program
{
private static BasePrinter printer;
private static ICommandEmitter e;
static void Main(string[] args)
{
Console.WriteLine("Welcome to the ESCPOS_NET Test Application!");
Console.Write("Would you like to see all debug messages? (y/n): ");
var response = Console.ReadLine().Trim().ToLowerInvariant();
var logLevel = LogLevel.Information;
if (response.Length >= 1 && response[0] == 'y')
{
Console.WriteLine("Debugging enabled!");
logLevel = LogLevel.Trace;
}
var factory = LoggerFactory.Create(b => b.AddConsole().SetMinimumLevel(logLevel));
var logger = factory.CreateLogger<Program>();
ESCPOS_NET.Logging.Logger = logger;
Console.WriteLine("1 ) Test Serial Port");
Console.WriteLine("2 ) Test Network Printer");
Console.Write("Choice: ");
string comPort = "";
string baudRate;
string ip;
string networkPort;
response = Console.ReadLine();
var valid = new List<string> { "1", "2" };
if (!valid.Contains(response))
{
response = "1";
}
int choice = int.Parse(response);
if (choice == 1)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
while (!comPort.StartsWith("COM"))
{
Console.Write("COM Port (enter for default COM5): ");
comPort = Console.ReadLine();
if (string.IsNullOrWhiteSpace(comPort))
{
comPort = "COM5";
}
}
Console.Write("Baud Rate (enter for default 115200): ");
baudRate = Console.ReadLine();
if (string.IsNullOrWhiteSpace(baudRate))
{
baudRate = "115200";
}
printer = new SerialPrinter(portName: comPort, baudRate: 115200);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Console.Write("File / virtual com path (eg. /dev/usb/lp0): ");
comPort = Console.ReadLine();
if (string.IsNullOrWhiteSpace(comPort))
{
comPort = "/dev/usb/lp0";
}
printer = new FilePrinter(filePath: comPort, false);
}
}
else if (choice == 2)
{
Console.Write("IP Address (eg. 192.168.1.240): ");
ip = Console.ReadLine();
if (string.IsNullOrWhiteSpace(ip))
{
ip = "192.168.254.202";
}
Console.Write("TCP Port (enter for default 9100): ");
networkPort = Console.ReadLine();
if (string.IsNullOrWhiteSpace(networkPort))
{
networkPort = "9100";
}
printer = new NetworkPrinter(settings: new NetworkPrinterSettings() { ConnectionString = $"{ip}:{networkPort}" });
}
bool monitor = false;
Thread.Sleep(500);
Console.Write("Turn on Live Status Back Monitoring? (y/n): ");
response = Console.ReadLine().Trim().ToLowerInvariant();
if (response.Length >= 1 && response[0] == 'y')
{
monitor = true;
}
e = new EPSON();
var testCases = new Dictionary<Option, string>()
{
{ Option.SingleLinePrinting, "Single Line Printing" },
{ Option.MultiLinePrinting, "Multi-line Printing" },
{ Option.LineSpacing, "Line Spacing" },
{ Option.BarcodeStyles, "Barcode Styles" },
{ Option.BarcodeTypes, "Barcode Types" },
{ Option.TwoDimensionCodes, "2D Codes" },
{ Option.TextStyles, "Text Styles" },
{ Option.FullReceipt, "Full Receipt" },
{ Option.CodePages, "Code Pages (Euro, Katakana, Etc)" },
{ Option.Images, "Images" },
{ Option.LegacyImages, "Legacy Images" },
{ Option.LargeByteArrays, "Large Byte Arrays" },
{ Option.CashDrawerPin2, "Cash Drawer Pin2" },
{ Option.CashDrawerPin5, "Cash Drawer Pin5" },
{ Option.Exit, "Exit" }
};
while (true)
{
foreach (var item in testCases)
{
Console.WriteLine($"{(int)item.Key} : {item.Value}");
}
Console.Write("Execute Test: ");
if (!int.TryParse(Console.ReadLine(), out choice) || !Enum.IsDefined(typeof(Option), choice))
{
Console.WriteLine("Invalid entry. Please try again.");
continue;
}
var enumChoice = (Option)choice;
if (enumChoice == Option.Exit)
{
return;
}
Console.Clear();
if (monitor)
{
printer.Write(e.Initialize());
printer.Write(e.Enable());
printer.Write(e.EnableAutomaticStatusBack());
}
Setup(monitor);
printer?.Write(e.PrintLine($"== [ Start {testCases[enumChoice]} ] =="));
switch (enumChoice)
{
case Option.SingleLinePrinting:
printer.Write(Tests.SingleLinePrinting(e));
break;
case Option.MultiLinePrinting:
printer.Write(Tests.MultiLinePrinting(e));
break;
case Option.LineSpacing:
printer.Write(Tests.LineSpacing(e));
break;
case Option.BarcodeStyles:
printer.Write(Tests.BarcodeStyles(e));
break;
case Option.BarcodeTypes:
printer.Write(Tests.BarcodeTypes(e));
break;
case Option.TwoDimensionCodes:
printer.Write(Tests.TwoDimensionCodes(e));
break;
case Option.TextStyles:
printer.Write(Tests.TextStyles(e));
break;
case Option.FullReceipt:
printer.Write(Tests.Receipt(e));
break;
case Option.Images:
printer.Write(Tests.Images(e, false));
break;
case Option.LegacyImages:
printer.Write(Tests.Images(e, true));
break;
case Option.LargeByteArrays:
try
{
printer.Write(Tests.TestLargeByteArrays(e));
}
catch (Exception e)
{
Console.WriteLine($"Aborting print due to test failure. Exception: {e?.Message}, Stack Trace: {e?.GetBaseException()?.StackTrace}");
}
break;
case Option.CashDrawerPin2:
printer.Write(Tests.CashDrawerOpenPin2(e));
break;
case Option.CashDrawerPin5:
printer.Write(Tests.CashDrawerOpenPin5(e));
break;
default:
Console.WriteLine("Invalid entry.");
break;
}
Setup(monitor);
printer?.Write(e.PrintLine($"== [ End {testCases[enumChoice]} ] =="));
printer?.Write(e.PartialCutAfterFeed(5));
// TODO: also make an automatic runner that runs all tests (command line).
}
}
public enum Option
{
SingleLinePrinting = 1,
MultiLinePrinting,
LineSpacing,
BarcodeStyles,
BarcodeTypes,
TwoDimensionCodes,
TextStyles,
FullReceipt,
CodePages,
Images,
LegacyImages,
LargeByteArrays,
CashDrawerPin2,
CashDrawerPin5,
Exit = 99
}
private static void StatusChanged(object sender, EventArgs ps)
{
var status = (PrinterStatusEventArgs)ps;
if (status == null) { Console.WriteLine("Status was null - unable to read status from printer."); return; }
Console.WriteLine($"Printer Online Status: {status.IsPrinterOnline}");
Console.WriteLine(JsonConvert.SerializeObject(status));
}
private static bool _hasEnabledStatusMonitoring = false;
private static void Setup(bool enableStatusBackMonitoring)
{
if (printer != null)
{
// Only register status monitoring once.
if (!_hasEnabledStatusMonitoring)
{
printer.StatusChanged += StatusChanged;
_hasEnabledStatusMonitoring = true;
}
printer?.Write(e.Initialize());
printer?.Write(e.Enable());
if (enableStatusBackMonitoring)
{
printer.Write(e.EnableAutomaticStatusBack());
}
}
}
}
}

View File

@@ -0,0 +1,43 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
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),
e.PrintLine(),
e.PrintLine("PDF417 (TINY):"),
e.Print2DCode(TwoDimensionCodeType.PDF417, websiteString, Size2DCode.TINY),
e.PrintLine(),
e.PrintLine("PDF417 (LARGE):"),
e.Print2DCode(TwoDimensionCodeType.PDF417, websiteString, Size2DCode.LARGE),
e.PrintLine(),
e.PrintLine("QRCODE MODEL 1:"),
e.Print2DCode(TwoDimensionCodeType.QRCODE_MODEL1, websiteString),
e.PrintLine(),
e.PrintLine("QRCODE MODEL 2:"),
e.PrintQRCode(websiteString),
e.PrintLine(),
e.PrintLine("QRCODE MICRO:"),
e.Print2DCode(TwoDimensionCodeType.QRCODE_MICRO, "github.com/lukevp"),
e.PrintLine(),
e.PrintLine("QRCODE MODEL 1 (TINY):"),
e.Print2DCode(TwoDimensionCodeType.QRCODE_MODEL1, websiteString, Size2DCode.TINY),
e.PrintLine(),
e.PrintLine("QRCODE MODEL 1 (LARGE):"),
e.Print2DCode(TwoDimensionCodeType.QRCODE_MODEL1, websiteString, Size2DCode.LARGE),
e.PrintLine()
};
}
}

View File

@@ -0,0 +1,64 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] BarcodeStyles(ICommandEmitter e) => new byte[][] {
//TODO: test all widths and put bar in front in label
e.PrintLine("Thinnest Width:"),
e.SetBarcodeHeightInDots(300),
e.SetBarWidth(BarWidth.Thinnest),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Thin Width:"),
e.SetBarcodeHeightInDots(300),
e.SetBarWidth(BarWidth.Thin),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Default Width:"),
e.SetBarcodeHeightInDots(300),
e.SetBarWidth(BarWidth.Default),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Thicker Width:"),
e.SetBarcodeHeightInDots(300),
e.SetBarWidth(BarWidth.Thick),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Thickest Width:"),
e.SetBarcodeHeightInDots(300),
e.SetBarWidth(BarWidth.Thickest),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Short (50 dots):"),
e.SetBarcodeHeightInDots(50),
e.SetBarWidth(BarWidth.Default),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Tall (255 dots):"),
e.SetBarcodeHeightInDots(255),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Label Above:"),
e.SetBarcodeHeightInDots(50),
e.SetBarLabelPosition(BarLabelPrintPosition.Above),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Label Above and Below:"),
e.SetBarLabelPosition(BarLabelPrintPosition.Both),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Label Below:"),
e.SetBarLabelPosition(BarLabelPrintPosition.Below),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905"),
e.PrintLine("Font B Label Below:"),
e.SetBarLabelFontB(true),
e.PrintBarcode(BarcodeType.UPC_A, "012345678905")
};
}
}

View File

@@ -0,0 +1,74 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] BarcodeTypes(ICommandEmitter e) => new byte[][] {
e.SetBarcodeHeightInDots(600),
e.SetBarWidth(BarWidth.Thinnest),
e.SetBarLabelPosition(BarLabelPrintPosition.Below),
e.PrintLine("CODABAR_NW_7:"),
e.PrintBarcode(BarcodeType.CODABAR_NW_7, "A31117013206375B"),
e.PrintLine("CODE128:"),
e.PrintBarcode(BarcodeType.CODE128, "ESC_POS_NET"),
e.PrintLine(),
e.PrintLine("CODE128 Type C:"),
e.PrintBarcode(BarcodeType.CODE128, "123456789101", BarcodeCode.CODE_C),
e.PrintLine(),
e.PrintLine("CODE39:"),
e.PrintBarcode(BarcodeType.CODE39, "*ESC-POS-NET*"),
e.PrintLine(),
e.PrintLine("CODE93:"),
e.PrintBarcode(BarcodeType.CODE93, "*ESC_POS_NET*"),
e.PrintLine(),
e.PrintLine("GS1_128:"),
e.PrintBarcode(BarcodeType.GS1_128, "(01)9501234567890*"),
e.PrintLine(),
e.PrintLine("GS1_DATABAR_EXPANDED:"),
e.PrintBarcode(BarcodeType.GS1_DATABAR_EXPANDED, "0001234567890"),
e.PrintLine(),
e.PrintLine("GS1_DATABAR_LIMITED:"),
e.PrintBarcode(BarcodeType.GS1_DATABAR_LIMITED, "0001234567890"),
e.PrintLine(),
e.PrintLine("GS1_DATABAR_OMNIDIRECTIONAL:"),
e.PrintBarcode(BarcodeType.GS1_DATABAR_OMNIDIRECTIONAL, "0001234567890"),
e.PrintLine(),
e.PrintLine("GS1_DATABAR_TRUNCATED:"),
e.PrintBarcode(BarcodeType.GS1_DATABAR_TRUNCATED, "0001234567890"),
e.PrintLine(),
e.PrintLine("ITF:"),
e.PrintBarcode(BarcodeType.ITF, "1234567895"),
e.PrintLine(),
e.PrintLine("JAN13_EAN13:"),
e.PrintBarcode(BarcodeType.JAN13_EAN13, "5901234123457"),
e.PrintLine(),
e.PrintLine("JAN8_EAN8:"),
e.PrintBarcode(BarcodeType.JAN8_EAN8, "96385074"),
e.PrintLine(),
e.PrintLine("UPC_A:"),
e.PrintBarcode(BarcodeType.UPC_A, "042100005264"),
e.PrintLine(),
e.PrintLine("UPC_E:"),
e.PrintBarcode(BarcodeType.UPC_E, "425261"),
e.PrintLine()
};
}
}

View File

@@ -0,0 +1,16 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] CashDrawerOpenPin2(ICommandEmitter e) => new byte[][] {
e.CashDrawerOpenPin2()
};
public static byte[][] CashDrawerOpenPin5(ICommandEmitter e) => new byte[][] {
e.CashDrawerOpenPin5()
};
}
}

View File

@@ -0,0 +1,45 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
using System.Collections.Generic;
using System.IO;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] CodePages(ICommandEmitter e, CodePage codePage) {
List<byte[]> test = new List<byte[]>();
test.Add(e.LeftAlign());
test.Add(e.PrintLine("Empty space = space or non-printable character."));
test.Add(e.PrintLine("Each row represents the first hex digit."));
test.Add(e.PrintLine("Each column represents the second hex digit."));
test.Add(e.PrintLine("For example, row 7, column A, represents"));
test.Add(e.PrintLine("the character with hex 0x7A."));
test.Add(e.PrintLine());
test.Add(e.PrintLine("Character Table for Code Page:"));
test.Add(e.PrintLine(codePage + " (Page " + ((int)codePage) + ")"));
test.Add(e.PrintLine("=========================="));
test.Add(e.PrintLine());
test.Add(e.PrintLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F "));
// Set CodePage to test codepage.
test.Add(e.CodePage(codePage));
for (int d1 = 0; d1 < 0x10; d1++)
{
var upperDigit = d1.ToString("x1").ToUpperInvariant();
test.Add(e.Print(upperDigit + " "));
for (int d2 = 0; d2 < 0x10; d2++)
{
var digit = d1 * 0x10 + d2;
if (digit <= 0x20) digit = 0x20;
test.Add(e.Print((char)digit + " "));
}
test.Add(e.PrintLine());
}
// Set CodePage back to default
test.Add(e.CodePage(CodePage.PC437_USA_STANDARD_EUROPE_DEFAULT));
test.Add(e.PrintLine());
return test.ToArray();
}
}
}

View File

@@ -0,0 +1,66 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
using System.IO;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] Images(ICommandEmitter e, bool isLegacy) => new byte[][] {
e.CenterAlign(),
e.PrintLine("Test PNG images with widths 100 - 600 px,"),
e.PrintLine("at native resolution"),
e.PrintLine("-- pd-logo-100.png --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-100.png"), true, isLegacy),
e.PrintLine("-- pd-logo-200.png --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-200.png"), true, isLegacy),
e.PrintLine("-- pd-logo-300.png --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy),
e.PrintLine("-- pd-logo-400.png --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-400.png"), true, isLegacy),
e.PrintLine("-- pd-logo-500.png --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-500.png"), true, isLegacy),
e.PrintLine("-- pd-logo-600.png --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-600.png"), true, isLegacy),
e.PrintLine("Test resizing 600 px image to 300px"),
e.PrintLine("-- pd-logo-600.png /300 --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-600.png"), true, isLegacy, 300),
e.PrintLine("Test image with taller height than width"),
e.PrintLine("-- abe-lincoln.png /300 --"),
e.PrintImage(File.ReadAllBytes("images/abe-lincoln.png"), true, isLegacy, 300),
e.PrintLine("Test 300px image resized to all remainders of 8,"),
e.PrintLine("to ensure overflow checking"),
e.PrintLine("-- pd-logo-300.png /296 (0 remainder) --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 296),
e.PrintLine("-- pd-logo-300.png /297 (1 remainder) --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 297),
e.PrintLine("-- pd-logo-300.png /298 (2 remainder) --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 298),
e.PrintLine("-- pd-logo-300.png /299 (3 remainder) --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 299),
e.PrintLine("-- pd-logo-300.png /300 (4 remainder) --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 300),
e.PrintLine("-- pd-logo-300.png /301 (5 remainder) --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 301),
e.PrintLine("-- pd-logo-300.png /302 (6 remainder) --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 302),
e.PrintLine("-- pd-logo-300.png /303 (7 remainder) --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 303),
e.PrintLine("Test 300px image in JPG format"),
e.PrintLine("-- pd-logo-300.jpg --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.jpg"), true, isLegacy),
e.PrintLine("Test 300px image in BMP format"),
e.PrintLine("-- pd-logo-300.jpg --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.bmp"), true, isLegacy),
e.PrintLine("Test 300px image in GIF format"),
e.PrintLine("-- pd-logo-300.jpg --"),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.gif"), true, isLegacy),
e.PrintLine("Test full color image converted"),
e.PrintLine("to black and white and interpolated"),
e.PrintLine("-- kitten.jpg --"),
e.PrintImage(File.ReadAllBytes("images/kitten.jpg"), true, isLegacy, 500),
e.PrintLine("-- portal companion cube.jpg --"),
e.PrintImage(File.ReadAllBytes("images/Portal_Companion_Cube.jpg"), true, isLegacy, 500)
};
}
}

View File

@@ -0,0 +1,105 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
using System;
using System.IO;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[] TestLargeByteArrays(ICommandEmitter e)
{
var kitten = e.PrintImage(File.ReadAllBytes("images/kitten.jpg"), true, true, 500);
var cube = e.PrintImage(File.ReadAllBytes("images/Portal_Companion_Cube.jpg"), true, true, 500);
var expectedResult = ByteSplicer.Combine(
e.CenterAlign(),
kitten,
cube,
kitten,
cube,
kitten,
cube,
kitten,
cube,
kitten,
cube,
kitten,
cube,
kitten,
cube,
kitten,
cube,
kitten,
cube,
kitten,
cube
);
MemoryPrinter mp = new MemoryPrinter();
mp.Write(expectedResult);
var response = mp.GetAllData();
bool hasErrors = false;
if (expectedResult.Length != response.Length)
{
Console.WriteLine($"Error: MemoryPrinter length mismatch - ${response.Length}, expected ${expectedResult.Length}");
hasErrors = true;
}
else
{
for (int i = 0; i < expectedResult.Length; i++)
{
if (expectedResult[i] != response[i])
{
Console.WriteLine($"Error: MemoryPrinter data mismatch - ${expectedResult[i]}, expected ${response[i]}, at location ${i}");
hasErrors = true;
}
}
}
if (!hasErrors)
{
Console.WriteLine("MemoryPrinter: ALL OK!");
}
else
{
Console.WriteLine("MemoryPrinter: Errors occured during testing, aborting!");
throw new ArgumentException();
}
Random r = new Random();
var filename = $"{r.NextDouble().ToString()}.tmp";
using (FilePrinter fp = new FilePrinter(filename, true))
{
fp.Write(expectedResult);
}
response = File.ReadAllBytes(filename);
hasErrors = false;
if (expectedResult.Length != response.Length)
{
Console.WriteLine($"Error: FilePrinter length mismatch - ${response.Length}, expected ${expectedResult.Length}");
hasErrors = true;
}
else
{
for (int i = 0; i < expectedResult.Length; i++)
{
if (expectedResult[i] != response[i])
{
Console.WriteLine($"Error: FilePrinter data mismatch - ${expectedResult[i]}, expected ${response[i]}, at location ${i}");
hasErrors = true;
}
}
}
if (!hasErrors)
{
Console.WriteLine("FilePrinter: ALL OK!");
}
else
{
Console.WriteLine("FilePrinter: Errors occured during testing, aborting!");
throw new ArgumentException();
}
return expectedResult;
}
}
}

View File

@@ -0,0 +1,18 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] LineSpacing(ICommandEmitter e) => new byte[][] {
e.SetLineSpacingInDots(200),
e.PrintLine("This is the default spacing."),
e.SetLineSpacingInDots(15),
e.PrintLine("This has 200 dots of spacing above."),
e.ResetLineSpacing(),
e.PrintLine("This has 15 dots of spacing above."),
e.PrintLine("This has the default spacing.")
};
}
}

View File

@@ -0,0 +1,25 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] MultiLinePrinting(ICommandEmitter e) => new byte[][] {
e.Print("Multiline Test: Windows...\r\nOSX...\rUnix...\n"),
//TODO: sanitize test.
e.PrintLine("Feeding 250 dots."),
e.FeedDots(250),
e.PrintLine("Feeding 3 lines."),
e.FeedLines(3),
e.PrintLine("Done Feeding."),
e.PrintLine("Reverse Feeding 6 lines."),
e.FeedLinesReverse(6),
e.PrintLine("Done Reverse Feeding.")
};
public static byte[][] SingleLinePrinting(ICommandEmitter e) => new byte[][] {
e.Print("Single Test Line Of Text\r\n"),
};
}
}

View File

@@ -0,0 +1,51 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
using System.IO;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] Receipt(ICommandEmitter e) => new byte[][] {
e.CenterAlign(),
e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true),
e.PrintLine(),
e.SetBarcodeHeightInDots(360),
e.SetBarWidth(BarWidth.Default),
e.SetBarLabelPosition(BarLabelPrintPosition.None),
e.PrintBarcode(BarcodeType.ITF, "0123456789"),
e.PrintLine("B&H PHOTO & VIDEO"),
e.PrintLine("420 NINTH AVE."),
e.PrintLine("NEW YORK, NY 10001"),
e.PrintLine("(212) 502-6380 - (800)947-9975"),
e.SetStyles(PrintStyle.Underline),
e.PrintLine("www.bhphotovideo.com"),
e.SetStyles(PrintStyle.None),
e.PrintLine(),
e.LeftAlign(),
e.PrintLine("Order: 123456789 Date: 02/01/19"),
e.PrintLine(),
e.PrintLine(),
e.SetStyles(PrintStyle.FontB),
e.PrintLine("1 TRITON LOW-NOISE IN-LINE MICROPHONE PREAMP"),
e.PrintLine(" TRFETHEAD/FETHEAD 89.95 89.95"),
e.PrintLine("----------------------------------------------------------------"),
e.RightAlign(),
e.PrintLine("SUBTOTAL 89.95"),
e.PrintLine("Total Order: 89.95"),
e.PrintLine("Total Payment: 89.95"),
e.PrintLine(),
e.LeftAlign(),
e.SetStyles(PrintStyle.Bold | PrintStyle.FontB),
e.PrintLine("SOLD TO: SHIP TO:"),
e.SetStyles(PrintStyle.FontB),
e.PrintLine(" LUKE PAIREEPINART LUKE PAIREEPINART"),
e.PrintLine(" 123 FAKE ST. 123 FAKE ST."),
e.PrintLine(" DECATUR, IL 12345 DECATUR, IL 12345"),
e.PrintLine(" (123)456-7890 (123)456-7890"),
e.PrintLine(" CUST: 87654321"),
e.PrintLine(),
e.PrintLine()
};
}
}

View File

@@ -0,0 +1,41 @@
using ESCPOS_NET.Emitters;
using ESCPOS_NET.Utilities;
namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] TextStyles(ICommandEmitter e) => new byte[][] {
e.SetStyles(PrintStyle.None),
e.Print("Default: The quick brown fox jumped over the lazy dogs.\n"),
e.SetStyles(PrintStyle.FontB),
e.Print("Font B: The quick brown fox jumped over the lazy dogs.\n"),
e.SetStyles(PrintStyle.Bold),
e.Print("Bold: The quick brown fox jumped over the lazy dogs.\n"),
e.SetStyles(PrintStyle.Underline),
e.Print("Underline: The quick brown fox jumped over the lazy dogs.\n"),
e.SetStyles(PrintStyle.DoubleWidth),
e.Print("DoubleWidth: The quick brown fox jumped over the lazy dogs.\n"),
e.SetStyles(PrintStyle.DoubleHeight),
e.Print("DoubleHeight: The quick brown fox jumped over the lazy dogs.\n"),
e.SetStyles(PrintStyle.FontB | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.Underline | PrintStyle.Bold),
e.Print("All Styles: The quick brown fox jumped over the lazy dogs.\n"),
e.SetStyles(PrintStyle.None),
e.ReverseMode(true),
e.PrintLine("REVERSE MODE: The quick brown fox jumped over the lazy dogs."),
e.SetStyles(PrintStyle.FontB | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth),
e.PrintLine("REVERSE MODE: The quick brown fox jumped over the lazy dogs."),
e.SetStyles(PrintStyle.None),
e.ReverseMode(false),
e.SetStyles(PrintStyle.None),
e.RightCharacterSpacing(5),
e.PrintLine("Right space 5: The quick brown fox jumped over the lazy dogs."),
e.RightCharacterSpacing(0),
e.SetStyles(PrintStyle.None),
e.UpsideDownMode(true),
e.PrintLine("Upside Down Mode: The quick brown fox jumped over the lazy dogs."),
e.UpsideDownMode(false)
};
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ns1="http://sozi.baierouge.fr"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
id="Layer_1"
sodipodi:docname="public-domain-logo-slightly-nicer.svg"
enable-background="new 0.995 1.495 88 31"
xml:space="preserve"
viewBox="0.995 1.495 671.6875 215.625"
version="1.1"
y="0px"
x="0px"
inkscape:version="0.48.3.1 r9886"
><sodipodi:namedview
id="namedview43"
fit-margin-left="0"
inkscape:zoom="0.3"
borderopacity="1"
inkscape:current-layer="Layer_1"
inkscape:cx="450.19344"
inkscape:cy="-104.995"
inkscape:window-maximized="1"
showgrid="false"
fit-margin-right="0"
bordercolor="#666666"
inkscape:window-x="0"
guidetolerance="10"
objecttolerance="10"
inkscape:window-y="24"
fit-margin-bottom="0"
inkscape:window-width="1920"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
pagecolor="#ffffff"
gridtolerance="10"
inkscape:window-height="1124"
showborder="false"
fit-margin-top="0"
/>
<g
id="g4285"
transform="translate(-224.16 380.12)"
><rect
id="rect3914-6"
style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:4.9835;fill:#ffffff"
rx="16.992"
ry="13.212"
height="210.65"
width="666.71"
y="-376.14"
x="227.64"
/><path
id="path11-9-3-7"
inkscape:connector-curvature="0"
d="m411.62-355.34c25.35 19.694 41.867 51.242 41.874 86.922-0.008 32.459-13.679 61.507-35.231 81.323h457.33v-168.24h-463.97z"
/><g
id="g15-9-3-0"
transform="matrix(8.4847 0 0 8.4847 226.81 -409.82)"
><path
id="path17-8-7-0"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m36.739 8.557c0.392 0 0.728 0.059 1.002 0.173 0.276 0.116 0.5 0.268 0.674 0.456 0.173 0.189 0.299 0.405 0.379 0.647 0.079 0.241 0.118 0.493 0.118 0.753 0 0.253-0.039 0.503-0.118 0.749-0.08 0.244-0.206 0.462-0.379 0.65-0.174 0.188-0.397 0.341-0.674 0.456-0.274 0.115-0.61 0.173-1.002 0.173h-1.452v2.267h-1.382v-6.324h2.834zm-0.379 2.976c0.158 0 0.312-0.012 0.457-0.035 0.147-0.023 0.276-0.069 0.388-0.137 0.111-0.068 0.201-0.164 0.269-0.288s0.101-0.287 0.101-0.487c0-0.201-0.033-0.363-0.101-0.488-0.067-0.123-0.157-0.22-0.269-0.286-0.111-0.069-0.24-0.114-0.388-0.138-0.146-0.024-0.299-0.036-0.457-0.036h-1.073v1.896l1.073-0.001z"
/><path
id="path19-2-5-5"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m44.751 14.398c-0.476 0.417-1.133 0.625-1.972 0.625-0.851 0-1.509-0.207-1.976-0.62-0.466-0.413-0.699-1.052-0.699-1.913v-3.933h1.381v3.934c0 0.171 0.016 0.339 0.045 0.506 0.029 0.165 0.091 0.311 0.185 0.438 0.094 0.125 0.225 0.229 0.392 0.309s0.392 0.119 0.673 0.119c0.493 0 0.833-0.109 1.021-0.332 0.188-0.221 0.282-0.568 0.282-1.04v-3.934h1.382v3.934c-0.001 0.855-0.239 1.491-0.714 1.907z"
/><path
id="path21-0-4-6"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m50.069 8.557c0.301 0 0.572 0.027 0.817 0.081 0.246 0.053 0.459 0.14 0.636 0.26 0.176 0.121 0.312 0.282 0.407 0.483 0.099 0.201 0.146 0.45 0.146 0.745 0 0.318-0.071 0.584-0.217 0.796-0.144 0.212-0.355 0.388-0.637 0.522 0.387 0.112 0.676 0.309 0.865 0.589 0.191 0.281 0.286 0.619 0.286 1.015 0 0.319-0.062 0.595-0.185 0.829-0.123 0.232-0.289 0.422-0.498 0.57-0.207 0.148-0.445 0.257-0.713 0.328-0.269 0.07-0.541 0.105-0.822 0.105h-3.044v-6.323h2.959zm-0.173 2.56c0.246 0 0.447-0.059 0.606-0.178 0.157-0.118 0.237-0.309 0.237-0.576 0-0.147-0.026-0.269-0.08-0.362-0.053-0.095-0.122-0.168-0.211-0.222-0.088-0.053-0.188-0.09-0.303-0.109-0.115-0.022-0.233-0.032-0.356-0.032h-1.294v1.479h1.401zm0.078 2.685c0.135 0 0.264-0.014 0.387-0.04 0.125-0.026 0.231-0.072 0.326-0.133 0.094-0.062 0.168-0.147 0.227-0.254 0.056-0.104 0.083-0.241 0.083-0.406 0-0.325-0.093-0.557-0.271-0.696-0.184-0.138-0.425-0.208-0.724-0.208h-1.505v1.737h1.477z"
/><path
id="path23-9-1-0"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m55.142 8.557v5.155h3.062v1.169h-4.444v-6.324h1.382z"
/><path
id="path25-4-8-8"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m60.748 8.557v6.324h-1.382v-6.324h1.382z"
/><path
id="path27-9-0-7"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m66.45 10.244c-0.082-0.132-0.184-0.248-0.307-0.349-0.125-0.101-0.265-0.179-0.421-0.235-0.153-0.057-0.315-0.085-0.487-0.085-0.312 0-0.574 0.062-0.791 0.183-0.217 0.12-0.396 0.283-0.527 0.486-0.137 0.204-0.232 0.436-0.296 0.695-0.062 0.26-0.093 0.529-0.093 0.806 0 0.267 0.031 0.525 0.093 0.776s0.159 0.477 0.296 0.677c0.134 0.201 0.311 0.361 0.527 0.483 0.217 0.12 0.479 0.181 0.791 0.181 0.424 0 0.754-0.129 0.99-0.389s0.383-0.602 0.437-1.028h1.337c-0.034 0.396-0.126 0.753-0.271 1.072-0.146 0.318-0.34 0.591-0.58 0.815s-0.521 0.395-0.846 0.513c-0.322 0.119-0.678 0.178-1.064 0.178-0.48 0-0.914-0.084-1.299-0.252-0.383-0.17-0.709-0.399-0.972-0.696-0.265-0.295-0.468-0.641-0.606-1.04-0.143-0.399-0.213-0.829-0.213-1.29 0-0.472 0.07-0.91 0.213-1.314 0.141-0.404 0.344-0.757 0.606-1.058 0.263-0.302 0.589-0.537 0.972-0.709 0.385-0.172 0.816-0.258 1.299-0.258 0.347 0 0.675 0.051 0.98 0.15 0.309 0.102 0.583 0.248 0.827 0.439 0.243 0.191 0.444 0.43 0.603 0.713 0.16 0.283 0.258 0.608 0.301 0.974h-1.339c-0.024-0.159-0.078-0.305-0.16-0.438z"
/><path
id="path29-7-0-4"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m36.615 17.415c0.405 0 0.782 0.064 1.131 0.193 0.35 0.131 0.651 0.326 0.906 0.586s0.455 0.584 0.599 0.975c0.144 0.389 0.216 0.848 0.216 1.372 0 0.462-0.059 0.888-0.176 1.274-0.118 0.391-0.295 0.728-0.532 1.012-0.238 0.283-0.534 0.506-0.89 0.668-0.354 0.162-0.772 0.244-1.254 0.244h-2.71v-6.324h2.71zm-0.096 5.154c0.199 0 0.393-0.031 0.581-0.097s0.354-0.174 0.502-0.323c0.146-0.151 0.264-0.349 0.352-0.59 0.088-0.242 0.132-0.537 0.132-0.887 0-0.316-0.031-0.605-0.093-0.863-0.062-0.257-0.162-0.478-0.304-0.658-0.141-0.186-0.326-0.324-0.559-0.422-0.231-0.099-0.517-0.146-0.858-0.146h-0.984v3.984h1.231v0.002z"
/><path
id="path31-9-8-2"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m40.8 19.289c0.141-0.405 0.344-0.759 0.606-1.06 0.265-0.304 0.589-0.537 0.973-0.709 0.385-0.172 0.816-0.258 1.298-0.258 0.487 0 0.921 0.086 1.303 0.258 0.383 0.172 0.705 0.405 0.969 0.709 0.265 0.301 0.467 0.652 0.605 1.06 0.143 0.403 0.213 0.843 0.213 1.313 0 0.461-0.07 0.892-0.213 1.288-0.141 0.397-0.343 0.746-0.605 1.041-0.264 0.296-0.586 0.526-0.969 0.694-0.382 0.168-0.814 0.253-1.303 0.253-0.481 0-0.913-0.085-1.298-0.253-0.384-0.168-0.708-0.398-0.973-0.694-0.263-0.295-0.466-0.644-0.606-1.041-0.141-0.396-0.211-0.827-0.211-1.288 0-0.47 0.07-0.91 0.211-1.313zm1.262 2.089c0.062 0.252 0.16 0.479 0.295 0.68 0.135 0.199 0.312 0.36 0.527 0.48 0.218 0.121 0.481 0.184 0.792 0.184 0.312 0 0.576-0.062 0.792-0.184 0.219-0.12 0.395-0.281 0.529-0.48 0.134-0.201 0.232-0.428 0.295-0.68 0.062-0.25 0.092-0.509 0.092-0.773 0-0.276-0.029-0.547-0.092-0.807-0.062-0.261-0.161-0.49-0.295-0.695-0.137-0.203-0.312-0.365-0.529-0.486-0.216-0.12-0.48-0.182-0.792-0.182-0.311 0-0.574 0.062-0.792 0.182-0.216 0.121-0.393 0.283-0.527 0.486-0.135 0.205-0.233 0.437-0.295 0.695s-0.093 0.527-0.093 0.807c0.001 0.264 0.032 0.523 0.093 0.773z"
/><path
id="path33-4-9-1"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m50.092 17.415 1.47 4.35h0.021l1.391-4.35h1.944v6.324h-1.294v-4.482h-0.019l-1.541 4.482h-1.062l-1.54-4.438h-0.019v4.438h-1.295v-6.324h1.944z"
/><path
id="path35-8-7-3"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m59.765 17.415 2.35 6.324h-1.436l-0.475-1.408h-2.35l-0.494 1.408h-1.392l2.377-6.324h1.42zm0.079 3.88-0.793-2.321h-0.019l-0.816 2.321h1.628z"
/><path
id="path37-6-1-3"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m64.547 17.415v6.324h-1.382v-6.324h1.382z"
/><path
id="path39-3-0-1"
style="fill:#ffffff"
inkscape:connector-curvature="0"
d="m67.603 17.415 2.623 4.242h0.018v-4.242h1.295v6.324h-1.383l-2.613-4.234h-0.018v4.234h-1.295v-6.324h1.373z"
/></g
><path
id="path3912-1-5"
d="m336.08-360.91c-51.67 0-93.562 40.444-93.562 90.336 0 49.885 41.892 90.336 93.57 90.336 51.67 0 93.562-40.452 93.562-90.336-0.008-49.893-41.9-90.336-93.57-90.336z"
style="stroke:#000000;stroke-width:10.267;fill:none"
inkscape:connector-curvature="0"
/><path
id="path27-9-4-1-6"
d="m359.71-299.7c-1.4695-2.3655-3.2974-4.4444-5.5016-6.2543-2.2401-1.81-4.7489-3.2077-7.5445-4.2113-2.7418-1.0215-5.645-1.5232-8.7273-1.5232-5.5912 0-10.286 1.111-14.175 3.2794-3.8887 2.1505-7.0965 5.0715-9.444 8.7094-2.4552 3.6558-4.1576 7.8133-5.3045 12.455-1.1111 4.6593-1.6666 9.4799-1.6666 14.444 0 4.7848 0.55549 9.4082 1.6666 13.906 1.1111 4.498 2.8494 8.5481 5.3045 12.132 2.4013 3.602 5.5732 6.4693 9.444 8.6556 3.8888 2.1505 8.5839 3.2436 14.175 3.2436 7.5983 0 13.512-2.3117 17.741-6.9711 4.2293-4.6593 6.8635-10.788 7.8313-18.422h23.96c-0.60926 7.0965-2.258 13.494-4.8564 19.211-2.6164 5.6987-6.093 10.591-10.394 14.605-4.3009 4.0142-9.3366 7.0786-15.161 9.1933-5.7704 2.1325-12.15 3.1898-19.067 3.1898-8.6019 0-16.379-1.5053-23.279-4.5159-6.8635-3.0465-12.706-7.1503-17.419-12.473-4.749-5.2866-8.3868-11.487-10.86-18.637-2.5626-7.1503-3.817-14.856-3.817-23.117 0-8.4585 1.2544-16.308 3.817-23.547 2.5268-7.2399 6.1647-13.566 10.86-18.96 4.7131-5.412 10.555-9.6233 17.419-12.706 6.8994-3.0823 14.623-4.6235 23.279-4.6235 6.2184 0 12.096 0.91395 17.562 2.6881 5.5374 1.8279 10.448 4.4443 14.82 7.8671 4.3547 3.4228 7.9568 7.7058 10.806 12.777 2.8672 5.0715 4.6235 10.896 5.394 17.455h-23.996c-0.42988-2.8493-1.3978-5.4658-2.8673-7.8492z"
style="fill:#000000"
inkscape:connector-curvature="0"
/><path
id="path4039-5-1"
d="m250.51-308.07 174.92 51.631"
style="stroke-linejoin:round;stroke:#000000;stroke-linecap:round;stroke-width:10.267;fill:none"
inkscape:connector-curvature="0"
/></g
><metadata
><rdf:RDF
><cc:Work
><dc:format
>image/svg+xml</dc:format
><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
/><cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
/><dc:publisher
><cc:Agent
rdf:about="http://openclipart.org/"
><dc:title
>Openclipart</dc:title
></cc:Agent
></dc:publisher
></cc:Work
><cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"
><cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction"
/><cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution"
/><cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
/></cc:License
></rdf:RDF
></metadata
></svg
>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,40 @@
// This code is adapted from a sample found at the URL
// "http://blogs.msdn.com/b/jmanning/archive/2004/12/19/325699.aspx"
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
namespace TcpEchoServer
{
public class TcpEchoServer
{
public static void Main()
{
Console.WriteLine("Starting echo server...");
int port = 9100;
TcpListener listener = new TcpListener(IPAddress.Loopback, port);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
NetworkStream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true };
StreamReader reader = new StreamReader(stream, Encoding.ASCII);
while (true)
{
string inputLine = "";
while (inputLine != null)
{
inputLine = reader.ReadLine();
writer.Write("E");
Console.WriteLine("Echoing string: " + inputLine);
}
Console.WriteLine("Server saw disconnect from client.");
}
}
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ESCPOS_NET\ESCPOS_NET.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,77 @@
using ESCPOS_NET.Emitters;
using Xunit;
namespace ESCPOS_NET.UnitTest.EmittersBased.EPSONTests
{
public class BarCode
{
private const string WEBSITE_STRING = "https://github.com/lukevp/ESC-POS-.NET/";
[Theory]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_15)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_25)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_30)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_7)]
[InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_7)]
public void PrintQRCode_Success(TwoDimensionCodeType codeType, Size2DCode size2DCode, CorrectionLevel2DCode correctionLevel2DCode)
{
var result = new byte[] { 29, 40, 107, 4, 0, 49, 65, (byte)codeType, 0, 29, 40, 107, 3, 0, 49, 67, (byte)size2DCode, 29, 40, 107, 3, 0, 49, 69, (byte)correctionLevel2DCode, 29, 40, 107, 42, 0, 49, 80, 48, 104, 116, 116, 112, 115, 58, 47, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109, 47, 108, 117, 107, 101, 118, 112, 47, 69, 83, 67, 45, 80, 79, 83, 45, 46, 78, 69, 84, 47, 29, 40, 107, 3, 0, 49, 81, 48 };
Assert.Equal(result, new EPSON().Print2DCode(codeType, WEBSITE_STRING, size2DCode, correctionLevel2DCode));
}
}
}

View File

@@ -0,0 +1,40 @@
using ESCPOS_NET.Emitters;
using System;
using System.Reflection;
using Xunit;
namespace ESCPOS_NET.UnitTest.GlobalTests
{
public class Barcode
{
private const string WEBSITE_STRING = "https://github.com/lukevp/ESC-POS-.NET/";
[Theory]
[InlineData("EPSON", TwoDimensionCodeType.QRCODE_MODEL1)]
[InlineData("EPSON", TwoDimensionCodeType.QRCODE_MODEL2)]
[InlineData("EPSON", TwoDimensionCodeType.QRCODE_MICRO)]
[InlineData("EPSON", null)]
public void PrintQRCode_Success(string emitter, TwoDimensionCodeType? codeType)
{
var type = Assembly.LoadFrom("ESCPOS_NET").GetType($"ESCPOS_NET.Emitters.{emitter}", true);
ICommandEmitter e = (ICommandEmitter)Activator.CreateInstance(type);
byte[] bytes;
if (codeType is TwoDimensionCodeType codeTypeValue)
bytes = e.PrintQRCode(WEBSITE_STRING, codeTypeValue);
else
bytes = e.PrintQRCode(WEBSITE_STRING);
Assert.True(bytes.Length > 0);
}
[Theory]
[InlineData("EPSON", TwoDimensionCodeType.PDF417)]
public void PrintQRCode_Failure(string emitter, TwoDimensionCodeType codeType)
{
var type = Assembly.LoadFrom("ESCPOS_NET").GetType($"ESCPOS_NET.Emitters.{emitter}", true);
ICommandEmitter e = (ICommandEmitter)Activator.CreateInstance(type);
Assert.Throws<ArgumentException>(() => e.PrintQRCode(WEBSITE_STRING, codeType));
}
}
}

View File

@@ -0,0 +1,37 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31515.178
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ESCPOS_NET", "ESCPOS_NET\ESCPOS_NET.csproj", "{BA181101-43A5-435E-8744-ACDA313D04D8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ESCPOS_NET.ConsoleTest", "ESCPOS_NET.ConsoleTest\ESCPOS_NET.ConsoleTest.csproj", "{958464FE-6B30-4BA7-A4B1-D63ED2E23158}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ESCPOS_NET.UnitTest", "ESCPOS_NET.UnitTest\ESCPOS_NET.UnitTest.csproj", "{8A848879-9776-4EBC-8484-41B8EE65FD8D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BA181101-43A5-435E-8744-ACDA313D04D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA181101-43A5-435E-8744-ACDA313D04D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA181101-43A5-435E-8744-ACDA313D04D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA181101-43A5-435E-8744-ACDA313D04D8}.Release|Any CPU.Build.0 = Release|Any CPU
{958464FE-6B30-4BA7-A4B1-D63ED2E23158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{958464FE-6B30-4BA7-A4B1-D63ED2E23158}.Debug|Any CPU.Build.0 = Debug|Any CPU
{958464FE-6B30-4BA7-A4B1-D63ED2E23158}.Release|Any CPU.ActiveCfg = Release|Any CPU
{958464FE-6B30-4BA7-A4B1-D63ED2E23158}.Release|Any CPU.Build.0 = Release|Any CPU
{8A848879-9776-4EBC-8484-41B8EE65FD8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A848879-9776-4EBC-8484-41B8EE65FD8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A848879-9776-4EBC-8484-41B8EE65FD8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A848879-9776-4EBC-8484-41B8EE65FD8D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FB679B5F-BE89-407A-820F-185FE53B15C5}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
/* FROM: https://www.codeproject.com/Tips/674256/ByteArrayBuilder-a-StringBuilder-for-Bytes 2/9/2019 */
namespace ESCPOS_NET.Utilities
{
/// <summary>
/// Provides similar functionality to a StringBuilder, but for bytes.
/// </summary>
/// <remarks>
/// To fill the builder, construct a new, empty builder, and call the
/// appropriate Append method overloads.
/// To read data from the builder, either use Rewind on an existing
/// builder, or construct a new builder by passing it the byte array
/// from a previous builder - which you can get with the ToArray
/// method.
/// </remarks>
/// <example>
///
/// ByteArrayBuilder bab = new ByteArrayBuilder();
/// string[] lines = File.ReadAllLines(@"D:\Temp\myText.txt");
/// bab.Append(lines.Length);
/// foreach (string s in lines)
/// {
/// bab.Append(s);
/// }
/// byte[] data = bab.ToArray();
/// ...
/// ByteArrayBuilder babOut = new ByteArrayBuilder(data);
/// int count = bab.GetInt();
/// string[] linesOut = new string[count];
/// for (int lineNo = 0; lineNo &lt; count; lineNo++)
/// {
/// linesOut[lineNo](babOut.GetString());
/// }
/// .
/// </example>
public class ByteArrayBuilder : IDisposable
{
/// <summary>
/// True in a byte form of the Line.
/// </summary>
private const byte StreamTrue = (byte)1;
/// <summary>
/// False in the byte form of a line.
/// </summary>
private const byte StreamFalse = (byte)0;
/// <summary>
/// Holds the actual bytes.
/// </summary>
private MemoryStream store = new MemoryStream();
/// <summary>
/// Gets bytes in the store.
/// </summary>
public int Length => (int)store.Length;
/// <summary>
/// Create a new, empty builder ready to be filled.
/// </summary>
public ByteArrayBuilder()
{
}
public ByteArrayBuilder Append(byte b)
{
AddBytes(new byte[] { b });
return this;
}
/// <summary>
/// Adds an IEnumerable of bytes to an array.
/// </summary>
/// <param name="b">Value to append to existing builder data.</param>
/// </param>
public ByteArrayBuilder Append(IEnumerable<byte> b)
{
if (b is byte[])
{
AddBytes((byte[])b);
return this;
}
AddBytes(b.ToArray());
return this;
}
/// <summary>
/// Clear all content from the builder.
/// </summary>
public void Clear()
{
store.Close();
store.Dispose();
store = new MemoryStream();
}
/// <summary>
/// Rewind the builder ready to read data.
/// </summary>
public void Rewind()
{
store.Seek(0, SeekOrigin.Begin);
}
/// <summary>
/// Set an absolute position in the builder.
/// **WARNING**
/// If you add any variable size objects to the builder, the results of
/// reading after a Seek to a non-zero value are unpredictable.
/// A builder does not store just objects - for some it stores additional
/// information as well.
/// </summary>
/// <param name="position">the position to seek.</param>
public void Seek(int position)
{
store.Seek((long)position, SeekOrigin.Begin);
}
/// <summary>
/// Returns the builder as an array of bytes.
/// </summary>
/// <returns>An array.</returns>
public byte[] ToArray()
{
byte[] data = new byte[Length];
Array.Copy(store.GetBuffer(), data, Length);
return data;
}
/// <summary>
/// Returns a text based (Base64) string version of the current content.
/// </summary>
/// <returns>The converted string.</returns>
public override string ToString()
{
return Convert.ToBase64String(ToArray());
}
/// <summary>
/// Add a string of raw bytes to the store.
/// </summary>
/// <param name="byteArray">the byte array.</param>
private void AddBytes(byte[] byteArray)
{
store.Write(byteArray, 0, byteArray.Length);
}
/// <summary>
/// Reads a specific number of bytes from the store.
/// </summary>
/// <param name="length">The length.</param>
/// <returns>The byte array.</returns>
private byte[] GetBytes(int length)
{
byte[] data = new byte[length];
if (length > 0)
{
int read = store.Read(data, 0, length);
if (read != length)
{
throw new ApplicationException("Buffer did not contain " + length + " bytes");
}
}
return data;
}
/// <summary>
/// Dispose of this builder and its resources.
/// </summary>
public void Dispose()
{
store.Close();
store.Dispose();
}
}
}

View File

@@ -0,0 +1,15 @@
namespace ESCPOS_NET
{
public static class ByteExtensions
{
public static bool IsBitSet(this byte b, int offset)
{
return (b & (1 << offset)) != 0;
}
public static bool IsBitNotSet(this byte b, int offset)
{
return (b & (1 << offset)) == 0;
}
}
}

View File

@@ -0,0 +1,15 @@
namespace ESCPOS_NET.Utilities
{
public static class ByteSplicer
{
public static byte[] Combine(params byte[][] byteArrays)
{
ByteArrayBuilder builder = new ByteArrayBuilder();
foreach (var byteArray in byteArrays)
{
builder.Append(byteArray);
}
return builder.ToArray();
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace ESCPOS_NET.DataValidation
{
public class DataConstraint
{
public int MinLength { get; set; }
public int MaxLength { get; set; }
public List<int> ValidLengths { get; set; }
public string ValidChars { get; set; }
}
}

View File

@@ -0,0 +1,193 @@
using ESCPOS_NET.Emitters;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ESCPOS_NET.DataValidation
{
public abstract class BaseDataValidator<T> where T : Enum
{
protected Dictionary<T, DataConstraint> _constraints;
public void Validate(T type, string data, BarcodeCode? code = null)
{
if (data is null)
{
throw new ArgumentNullException(nameof(data));
}
// Validate constraints on barcode.
_constraints.TryGetValue(type, out var constraints);
if (constraints is null)
{
return;
}
// Check lengths
if (constraints.ValidLengths != null)
{
if (!constraints.ValidLengths.Contains(data.Length))
{
throw new ArgumentException($"Code '{data}' is not a valid length from: [{string.Join(", ", constraints.ValidLengths)}].");
}
}
else if (data.Length < constraints.MinLength)
{
throw new ArgumentException($"Code '{data}' is shorter than minimum length {constraints.MinLength}.");
}
else if (data.Length > constraints.MaxLength)
{
throw new ArgumentException($"Code '{data}' is longer than maximum length {constraints.MaxLength}.");
}
// Check if barcode contains invalid characters.
if (constraints.ValidChars == "7BIT-ASCII")
{
if (!data.All(x => x <= 127 && x >= 0))
{
throw new ArgumentException($"Code '{data}' contained invalid characters not in: {constraints.ValidChars}.");
}
}
else if (constraints.ValidChars != null && !data.All(x => constraints.ValidChars.Contains(x)))
{
throw new ArgumentException($"Code '{data}' contained invalid characters not in: {constraints.ValidChars}.");
}
RunSpecificValidations(type, data, code);
}
protected abstract void RunSpecificValidations(T type, string data, BarcodeCode? code);
}
public static class DataValidator
{
private static BarcodeDataValidator singletonBarcode = null;
private static TwoDimensionCodeDataValidator singleton2DCode = null;
public static void ValidateBarcode(BarcodeType type, BarcodeCode code, string data)
{
if (singletonBarcode is null)
{
singletonBarcode = new BarcodeDataValidator();
}
singletonBarcode.Validate(type, data, code);
}
public static void Validate2DCode(TwoDimensionCodeType type, string data)
{
if (singleton2DCode is null)
{
singleton2DCode = new TwoDimensionCodeDataValidator();
}
singleton2DCode.Validate(type, data);
}
private class BarcodeDataValidator : BaseDataValidator<BarcodeType>
{
public BarcodeDataValidator()
{
_constraints = new Dictionary<BarcodeType, DataConstraint>()
{
{ BarcodeType.UPC_A, new DataConstraint() { MinLength = 11, MaxLength = 12, ValidChars = "0123456789" } },
{ BarcodeType.UPC_E, new DataConstraint() { ValidLengths = new List<int>() { 6, 7, 8, 11, 12 }, ValidChars = "0123456789" } },
{ BarcodeType.JAN13_EAN13, new DataConstraint() { MinLength = 12, MaxLength = 13, ValidChars = "0123456789" } },
{ BarcodeType.JAN8_EAN8, new DataConstraint() { MinLength = 7, MaxLength = 8, ValidChars = "0123456789" } },
{ BarcodeType.CODE39, new DataConstraint() { MinLength = 1, MaxLength = 255, ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./" } },
{ BarcodeType.ITF, new DataConstraint() { MinLength = 2, MaxLength = 255, ValidChars = "0123456789" } },
{ BarcodeType.CODABAR_NW_7, new DataConstraint() { MinLength = 2, MaxLength = 255, ValidChars = "0123456789ABCDabcd$+-./:" } },
{ BarcodeType.CODE93, new DataConstraint() { MinLength = 1, MaxLength = 255, ValidChars = "7BIT-ASCII" } },
{ BarcodeType.CODE128, new DataConstraint() { MinLength = 1, MaxLength = 253, ValidChars = "7BIT-ASCII" } },
{ BarcodeType.GS1_128, new DataConstraint() { MinLength = 1, MaxLength = 253, ValidChars = "7BIT-ASCII" } },
{ BarcodeType.GS1_DATABAR_OMNIDIRECTIONAL, new DataConstraint() { MinLength = 13, MaxLength = 13, ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./" } },
{ BarcodeType.GS1_DATABAR_TRUNCATED, new DataConstraint() { MinLength = 13, MaxLength = 13, ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./" } },
{ BarcodeType.GS1_DATABAR_LIMITED, new DataConstraint() { MinLength = 13, MaxLength = 13, ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./" } },
{ BarcodeType.GS1_DATABAR_EXPANDED, new DataConstraint() { MinLength = 2, MaxLength = 255, ValidChars = "0123456789ABCDabcd !\"%$'()*+,-./:;<=>?_{" } },
};
}
protected override void RunSpecificValidations(BarcodeType type, string barcode, BarcodeCode? code)
{
switch (type)
{
case BarcodeType.UPC_E:
if (barcode.Length != 6 && !barcode.StartsWith("0", StringComparison.InvariantCulture))
{
throw new ArgumentException($"UPC_E Barcode {barcode} with length of 7, 8, 11, or 12 must start with 0.");
}
break;
case BarcodeType.ITF:
if (barcode.Length % 2 != 0)
{
throw new ArgumentException($"ITF Barcode {barcode} has length {barcode.Length}, which is not an even number.");
}
break;
case BarcodeType.CODABAR_NW_7:
if (!"ABCD".Contains(barcode[0]) || !"ABCD".Contains(barcode[barcode.Length - 1]))
{
throw new ArgumentException($"CODABAR_NW_7 Barcode {barcode} must start and end with an ABCD character.");
}
if (barcode.Skip(1).Take(barcode.Length - 2).Any(x => "ABCD".Contains(x)))
{
throw new ArgumentException($"CODABAR_NW_7 Barcode {barcode} must not include ABCD characters in the body of the barcode.");
}
break;
case BarcodeType.CODE93:
if (!barcode.StartsWith("*", StringComparison.InvariantCulture) || !barcode.EndsWith("*", StringComparison.InvariantCulture))
{
throw new ArgumentException($"CODE93 Barcode {barcode} must start and end with * characters.");
}
break;
case BarcodeType.CODE128:
if (code == BarcodeCode.CODE_C)
{
if (barcode.Length % 2 != 0)
{
throw new ArgumentException($"{nameof(barcode)} length must be divisible by 2");
}
if (!barcode.All(x => x <= '9' && x >= '0'))
{
throw new ArgumentException($"Barcode {barcode} is invalid. CODE128 CODE_C barcodes only support numeric characters.");
}
}
break;
}
}
}
private class TwoDimensionCodeDataValidator : BaseDataValidator<TwoDimensionCodeType>
{
public TwoDimensionCodeDataValidator()
{
_constraints = new Dictionary<TwoDimensionCodeType, DataConstraint>()
{
{ TwoDimensionCodeType.PDF417, new DataConstraint() { MinLength = 0, MaxLength = 255 } },
{ TwoDimensionCodeType.QRCODE_MODEL1, new DataConstraint() { MinLength = 0, MaxLength = 707 } },
{ TwoDimensionCodeType.QRCODE_MODEL2, new DataConstraint() { MinLength = 0, MaxLength = 4296 } },
{ TwoDimensionCodeType.QRCODE_MICRO, new DataConstraint() { MinLength = 0, MaxLength = 21 } },
};
}
// TODO: Research specific validations for QRCode & PDF417
protected override void RunSpecificValidations(TwoDimensionCodeType type, string barcode, BarcodeCode? code)
{
if (code != null)
{
throw new ArgumentException($"Barcode code should be always null for 2D Codes.", nameof(code));
}
}
}
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Company>animalistic.io</Company>
<Product>ESCPOS_NET</Product>
<Authors>Luke Paireepinart</Authors>
<Description>.NET Standard 2.0 Implementation of ESC/POS command generation and integration with thermal printers.
Allows creating receipts with all common functionality supported.</Description>
<Copyright>Copyright 2021 CandL Development, LLC.</Copyright>
<PackageLicenseUrl></PackageLicenseUrl>
<RepositoryUrl>https://github.com/lukevp/ESC-POS-.NET</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>epson thermal receipt printing printer usb wifi bluetooth serial esc pos escpos escp esc/pos esc/pos.net esc_pos esc_pos_net esc_pos_usb esc_pos_usb_net</PackageTags>
<Version>2.0.0</Version>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>2.0.0.0</FileVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/lukevp/ESC-POS-.NET</PackageProjectUrl>
<PackageReleaseNotes>- Completely rebuilt the network printer code</PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0" />
<PackageReference Include="SuperSimpleTcp" Version="2.4.0" />
<PackageReference Include="System.IO.Ports" Version="4.6.0" />
<PackageReference Include="System.Text.Json" Version="5.0.2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,16 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
/* Action Commands */
public virtual byte[] FullCut() => new byte[] { Cmd.GS, Ops.PaperCut, Functions.PaperCutFullCut };
public virtual byte[] PartialCut() => new byte[] { Cmd.GS, Ops.PaperCut, Functions.PaperCutPartialCut };
public virtual byte[] FullCutAfterFeed(int lineCount) => new byte[] { Cmd.GS, Ops.PaperCut, Functions.PaperCutFullCutWithFeed, (byte)lineCount };
public virtual byte[] PartialCutAfterFeed(int lineCount) => new byte[] { Cmd.GS, Ops.PaperCut, Functions.PaperCutPartialCutWithFeed, (byte)lineCount };
}
}

View File

@@ -0,0 +1,115 @@
using ESCPOS_NET.DataValidation;
using ESCPOS_NET.Emitters.BaseCommandValues;
using ESCPOS_NET.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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);
return BarcodeBytes(type, barcode, code);
}
protected virtual byte[] BarcodeBytes(BarcodeType type, string barcode, BarcodeCode code)
{
// For CODE128, prepend the first 2 characters as 0x7B and the CODE type, and escape 0x7B characters.
if (type == BarcodeType.CODE128)
{
if (code == BarcodeCode.CODE_C)
{
byte[] b = Encoding.ASCII.GetBytes(barcode);
byte[] ob = new byte[b.Length / 2];
for (int i = 0, obc = 0; i < b.Length; i += 2)
{
ob[obc++] = (byte)(((b[i] - '0') * 10) + (b[i + 1] - '0'));
}
barcode = Encoding.ASCII.GetString(ob);
}
barcode = barcode.Replace("{", "{{");
barcode = $"{(char)0x7B}{(char)code}" + barcode;
}
var command = new List<byte> { Cmd.GS, Barcodes.PrintBarcode, (byte)type, (byte)barcode.Length };
command.AddRange(barcode.ToCharArray().Select(x => (byte)x));
return command.ToArray();
}
public virtual byte[] PrintQRCode(string data, TwoDimensionCodeType type = TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode size = Size2DCode.NORMAL, CorrectionLevel2DCode correction = CorrectionLevel2DCode.PERCENT_7)
{
if (type == TwoDimensionCodeType.PDF417)
{
throw new ArgumentException($"{nameof(TwoDimensionCodeType.PDF417)} is not a valid QRCode type. Please use {nameof(Print2DCode)} method", nameof(type));
}
return Print2DCode(type, data, size, correction);
}
public virtual byte[] Print2DCode(TwoDimensionCodeType type, string data, Size2DCode size = Size2DCode.NORMAL, CorrectionLevel2DCode correction = CorrectionLevel2DCode.PERCENT_7)
{
DataValidator.Validate2DCode(type, data);
return TwoDimensionCodeBytes(type, data, size, correction);
}
protected virtual byte[] TwoDimensionCodeBytes(TwoDimensionCodeType type, string data, Size2DCode size, CorrectionLevel2DCode correction)
{
List<byte> command = new List<byte>();
byte[] initial = { Cmd.GS, Barcodes.Set2DCode, Barcodes.PrintBarcode };
switch (type)
{
case TwoDimensionCodeType.PDF417:
command.AddRange(initial, Barcodes.SetPDF417NumberOfColumns, Barcodes.AutoEnding);
command.AddRange(initial, Barcodes.SetPDF417NumberOfRows, Barcodes.AutoEnding);
command.AddRange(initial, Barcodes.SetPDF417DotSize, (byte)size);
command.AddRange(initial, Barcodes.SetPDF417CorrectionLevel, (byte)correction);
// k = (pL + pH * 256) - 3 --> But pH is always 0.
int k = data.Length;
int l = k + 3;
command.AddRange(initial, (byte)l, Barcodes.StorePDF417Data);
command.AddRange(data.ToCharArray().Select(x => (byte)x));
// Prints stored PDF417
command.AddRange(initial, Barcodes.PrintPDF417);
break;
case TwoDimensionCodeType.QRCODE_MODEL1:
case TwoDimensionCodeType.QRCODE_MODEL2:
case TwoDimensionCodeType.QRCODE_MICRO:
command.AddRange(initial, Barcodes.SelectQRCodeModel, (byte)type, Barcodes.AutoEnding);
command.AddRange(initial, Barcodes.SetQRCodeDotSize, (byte)size);
command.AddRange(initial, Barcodes.SetQRCodeCorrectionLevel, (byte)correction);
int num = data.Length + 3;
int pL = num % 256;
int pH = num / 256;
command.AddRange(initial, (byte)pL, (byte)pH, Barcodes.StoreQRCodeData);
command.AddRange(data.ToCharArray().Select(x => (byte)x));
// Prints stored QRCode
command.AddRange(initial, Barcodes.PrintQRCode);
break;
default:
throw new NotImplementedException($"2D Code '{type}' was not implemented yet.");
}
return command.ToArray();
}
public virtual byte[] SetBarcodeHeightInDots(int height) => new byte[] { Cmd.GS, Barcodes.SetBarcodeHeightInDots, (byte)height };
public virtual byte[] SetBarWidth(BarWidth width) => new byte[] { Cmd.GS, Barcodes.SetBarWidth, (byte)width };
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) };
}
}

View File

@@ -0,0 +1,16 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
/* Cash Drawer Commands */
public virtual byte[] CashDrawerOpenPin2(int pulseOnTimeMs = 120, int pulseOffTimeMs = 240) {
return new byte[] { Cmd.ESC, Ops.CashDrawerPulse, 0x00, (byte) (pulseOnTimeMs / 2), (byte) (pulseOffTimeMs / 2) };
}
public virtual byte[] CashDrawerOpenPin5(int pulseOnTimeMs = 120, int pulseOffTimeMs = 240) {
return new byte[] { Cmd.ESC, Ops.CashDrawerPulse, 0x01, (byte) (pulseOnTimeMs / 2), (byte) (pulseOffTimeMs / 2) };
}
}
}

View File

@@ -0,0 +1,22 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
/* Character Commands */
public virtual byte[] SetStyles(PrintStyle style) => new byte[] { Cmd.ESC, Chars.StyleMode, (byte)style };
public virtual byte[] LeftAlign() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.Left };
public virtual byte[] CenterAlign() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.Center };
public virtual byte[] RightAlign() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.Right };
public virtual byte[] RightCharacterSpacing(int spaceCount) => new byte[] { Cmd.ESC, Chars.RightCharacterSpacing, (byte)spaceCount };
public virtual byte[] CodePage(CodePage codePage) => new byte[] { Cmd.ESC, Chars.CodePage, (byte)codePage };
public virtual byte[] Color(Color color) => new byte[] { Cmd.ESC, Chars.Color, (byte)color };
}
}

View File

@@ -0,0 +1,10 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
/* Display Commands */
public virtual byte[] Clear() => new byte[] { Display.CLR };
}
}

View File

@@ -0,0 +1,122 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
using ESCPOS_NET.Utilities;
using SixLabors.ImageSharp;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
private byte[] GetImageHeader(int commandLength)
{
byte[] lengths = new byte[4];
int i = 0;
while (commandLength > 0)
{
lengths[i] = (byte)(commandLength & 0xFF);
commandLength >>= 8;
i++;
}
if (i >= 3)
{
return new byte[] { Cmd.GS, Images.ImageCmd8, Images.ImageCmdL, lengths[0], lengths[1], lengths[2], lengths[3] };
}
else
{
return new byte[] { Cmd.GS, Images.ImageCmdParen, Images.ImageCmdL, lengths[0], lengths[1] };
}
}
/* Image Commands */
public virtual byte[] SetImageDensity(bool isHiDPI)
{
ByteArrayBuilder builder = new ByteArrayBuilder();
byte dpiSetting = isHiDPI ? (byte)0x33 : (byte)0x32; // TODO: is this right??
byte[] baseCommand = new byte[] { 0x30, 0x31, dpiSetting, dpiSetting };
builder.Append(GetImageHeader(baseCommand.Length));
builder.Append(baseCommand);
return builder.ToArray();
}
public virtual byte[] BufferImage(byte[] image, int maxWidth = -1, bool isLegacy = false, int color = 1)
{
ByteArrayBuilder imageCommand = new ByteArrayBuilder();
byte colorByte;
switch (color)
{
case 2:
colorByte = 0x32;
break;
case 3:
colorByte = 0x33;
break;
default:
colorByte = 0x31;
break;
}
int width;
int height;
byte[] imageData;
using (var img = Image.Load(image))
{
imageData = img.ToSingleBitPixelByteArray(maxWidth: maxWidth == -1 ? (int?)null : maxWidth);
height = img.Height;
width = img.Width;
}
byte heightL = (byte)height;
byte heightH = (byte)(height >> 8);
if (isLegacy)
{
var byteWidth = (width + 7 & -8) / 8;
byte widthL = (byte)byteWidth;
byte widthH = (byte)(byteWidth >> 8);
imageCommand.Append(new byte[] { Cmd.GS, Images.ImageCmdLegacy, 0x30, 0x00, widthL, widthH, heightL, heightH });
}
else
{
byte widthL = (byte)width;
byte widthH = (byte)(width >> 8);
imageCommand.Append(new byte[] { 0x30, 0x70, 0x30, 0x01, 0x01, colorByte, widthL, widthH, heightL, heightH });
}
imageCommand.Append(imageData);
// Load image to print buffer
ByteArrayBuilder response = new ByteArrayBuilder();
byte[] imageCommandBytes = imageCommand.ToArray();
if (!isLegacy)
{
response.Append(GetImageHeader(imageCommandBytes.Length));
}
response.Append(imageCommandBytes);
return response.ToArray();
}
public virtual byte[] WriteImageFromBuffer()
{
// Print image that's already buffered.
ByteArrayBuilder response = new ByteArrayBuilder();
byte[] printCommandBytes = new byte[] { 0x30, 0x32 };
response.Append(GetImageHeader(printCommandBytes.Length));
response.Append(printCommandBytes);
return response.ToArray();
}
public virtual byte[] PrintImage(byte[] image, bool isHiDPI, bool isLegacy = false, int maxWidth = -1, int color = 1)
{
if (isLegacy)
{
return ByteSplicer.Combine(BufferImage(image, maxWidth, isLegacy));
}
else
{
return ByteSplicer.Combine(SetImageDensity(isHiDPI), BufferImage(image, maxWidth, isLegacy, color), WriteImageFromBuffer());
}
}
}
}

View File

@@ -0,0 +1,11 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
public virtual byte[] ResetLineSpacing() => new byte[] { Cmd.ESC, Whitespace.ResetLineSpacing };
public virtual byte[] SetLineSpacingInDots(int dots) => new byte[] { Cmd.ESC, Whitespace.LineSpacingInDots, (byte)dots };
}
}

View File

@@ -0,0 +1,14 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
/* Operational Commands */
public virtual byte[] Initialize() => new byte[] { Cmd.ESC, Ops.Initialize };
public virtual byte[] Enable() => new byte[] { Cmd.ESC, Ops.EnableDisable, 1 };
public virtual byte[] Disable() => new byte[] { Cmd.ESC, Ops.EnableDisable, 0 };
}
}

View File

@@ -0,0 +1,39 @@
using System.Linq;
using ESCPOS_NET.Emitters.BaseCommandValues;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
/* Printing Commands */
public virtual byte[] Print(string data)
{
// Fix OSX or Windows-style newlines
data = data.Replace("\r\n", "\n");
data = data.Replace("\r", "\n");
// TODO: Sanitize...
return data.ToCharArray().Select(x => (byte)x).ToArray();
}
public virtual byte[] PrintLine(string line)
{
if (line == null)
{
return Print("\n");
}
return Print(line.Replace("\r", string.Empty).Replace("\n", string.Empty) + "\n");
}
public virtual byte[] FeedLines(int lineCount) => new byte[] { Cmd.ESC, Whitespace.FeedLines, (byte)lineCount };
public virtual byte[] FeedLinesReverse(int lineCount) => new byte[] { Cmd.ESC, Whitespace.FeedLinesReverse, (byte)lineCount };
public virtual byte[] FeedDots(int dotCount) => new byte[] { Cmd.ESC, Whitespace.FeedDots, (byte)dotCount };
public virtual byte[] ReverseMode(bool enable) => new byte[] { Cmd.GS, Chars.ReversePrintMode, enable ? (byte)0x01 : (byte)0x00 };
public virtual byte[] UpsideDownMode(bool enable) => new byte[] { Cmd.ESC, Chars.UpsideDownMode, enable ? (byte)0x01 : (byte)0x00 };
}
}

View File

@@ -0,0 +1,18 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
/* Status Commands */
public virtual byte[] EnableAutomaticStatusBack() => new byte[] { Cmd.GS, Status.AutomaticStatusBack, 0xFF };
public virtual byte[] EnableAutomaticInkStatusBack() => new byte[] { Cmd.GS, Status.AutomaticInkStatusBack, 0xFF };
public virtual byte[] DisableAutomaticStatusBack() => new byte[] { Cmd.GS, Status.AutomaticStatusBack, 0x00 };
public virtual byte[] DisableAutomaticInkStatusBack() => new byte[] { Cmd.GS, Status.AutomaticInkStatusBack, 0x00 };
public virtual byte[] RequestPaperStatus() => new byte[] { Cmd.GS, Status.RequestStatus, 0x31 };
public virtual byte[] RequestDrawerStatus() => new byte[] { Cmd.GS, Status.RequestStatus, 0x32 };
public virtual byte[] RequestInkStatus() => new byte[] { Cmd.GS, Status.RequestStatus, 0x34 };
}
}

View File

@@ -0,0 +1,26 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Barcodes
{
public static readonly byte PrintBarcode = 0x6B;
public static readonly byte SetBarcodeHeightInDots = 0x68;
public static readonly byte SetBarWidth = 0x77;
public static readonly byte SetBarLabelPosition = 0x48;
public static readonly byte SetBarLabelFont = 0x66;
public static readonly byte Set2DCode = 0x28;
public static readonly byte AutoEnding = 0x00;
public static readonly byte[] SetPDF417NumberOfColumns = { 0x03, 0x00, 0x30, 0x41 };
public static readonly byte[] SetPDF417NumberOfRows = { 0x03, 0x00, 0x30, 0x42 };
public static readonly byte[] SetPDF417DotSize = { 0x03, 0x00, 0x30, 0x43 };
public static readonly byte[] SetPDF417CorrectionLevel = { 0x04, 0x00, 0x30, 0x45, 0x30 };
public static readonly byte[] StorePDF417Data = { 0x00, 0x30, 0x50, 0x30 };
public static readonly byte[] PrintPDF417 = { 0x03, 0x00, 0x30, 0x51, 0x30 };
public static readonly byte[] SelectQRCodeModel = { 0x04, 0x00, 0x31, 0x41 };
public static readonly byte[] SetQRCodeDotSize = { 0x03, 0x00, 0x31, 0x43 };
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 };
}
}

View File

@@ -0,0 +1,13 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Chars
{
public static readonly byte StyleMode = 0x21;
public static readonly byte Alignment = 0x61;
public static readonly byte ReversePrintMode = 0x42;
public static readonly byte RightCharacterSpacing = 0x20;
public static readonly byte UpsideDownMode = 0x7B;
public static readonly byte CodePage = 0x74;
public static readonly byte Color = 0x72;
}
}

View File

@@ -0,0 +1,8 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Cmd
{
public static readonly byte ESC = 0x1B;
public static readonly byte GS = 0x1D;
}
}

View File

@@ -0,0 +1,7 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Display
{
public static readonly byte CLR = 0x0C;
}
}

View File

@@ -0,0 +1,10 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Functions
{
public static readonly byte PaperCutFullCut = 0x00;
public static readonly byte PaperCutFullCutWithFeed = 0x41;
public static readonly byte PaperCutPartialCut = 0x01;
public static readonly byte PaperCutPartialCutWithFeed = 0x42;
}
}

View File

@@ -0,0 +1,10 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Images
{
public static readonly byte ImageCmdParen = 0x28;
public static readonly byte ImageCmdLegacy = 0x76;
public static readonly byte ImageCmd8 = 0x38;
public static readonly byte ImageCmdL = 0x4C;
}
}

View File

@@ -0,0 +1,10 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Ops
{
public static readonly byte Initialize = 0x40;
public static readonly byte EnableDisable = 0x3D;
public static readonly byte PaperCut = 0x56;
public static readonly byte CashDrawerPulse = 0x70;
}
}

View File

@@ -0,0 +1,9 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Status
{
public static readonly byte AutomaticStatusBack = 0x61;
public static readonly byte AutomaticInkStatusBack = 0x6A;
public static readonly byte RequestStatus = 0x72;
}
}

View File

@@ -0,0 +1,13 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class Whitespace
{
// TODO: tabs?
public static readonly byte Linefeed = 0x0A;
public static readonly byte FeedLines = 0x64;
public static readonly byte FeedLinesReverse = 0x65;
public static readonly byte FeedDots = 0x4A;
public static readonly byte ResetLineSpacing = 0x32;
public static readonly byte LineSpacingInDots = 0x33;
}
}

View File

@@ -0,0 +1,6 @@
namespace ESCPOS_NET.Emitters
{
public class EPSON : BaseCommandEmitter
{
}
}

View File

@@ -0,0 +1,30 @@
namespace ESCPOS_NET.Emitters
{
[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 TwoDimensionCodeType
{
PDF417 = 0,
QRCODE_MODEL1 = 49,
QRCODE_MODEL2 = 50,
QRCODE_MICRO = 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 Size2DCode
{
TINY = 2,
SMALL = 3,
NORMAL = 4,
LARGE = 5,
EXTRA = 6,
}
[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 CorrectionLevel2DCode
{
PERCENT_7 = 48,
PERCENT_15 = 49,
PERCENT_25 = 50,
PERCENT_30 = 51,
}
}

View File

@@ -0,0 +1,10 @@
namespace ESCPOS_NET.Emitters
{
[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 Align
{
Left = 0,
Center = 1,
Right = 2,
}
}

View File

@@ -0,0 +1,11 @@
namespace ESCPOS_NET.Emitters
{
[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 BarLabelPrintPosition
{
None = 0,
Above = 1,
Below = 2,
Both = 3,
}
}

View File

@@ -0,0 +1,12 @@
namespace ESCPOS_NET.Emitters
{
[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 BarWidth
{
Thinnest = 2,
Thin = 3,
Default = 4,
Thick = 5,
Thickest = 6,
}
}

View File

@@ -0,0 +1,9 @@
namespace ESCPOS_NET.Emitters
{
public enum BarcodeCode
{
CODE_A = 0x41,
CODE_B = 0x42,
CODE_C = 0x43,
}
}

View File

@@ -0,0 +1,21 @@
namespace ESCPOS_NET.Emitters
{
[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 BarcodeType // All from Function B
{
UPC_A = 0x41,
UPC_E = 0x42,
JAN13_EAN13 = 0x43,
JAN8_EAN8 = 0x44,
CODE39 = 0x45,
ITF = 0x46,
CODABAR_NW_7 = 0x47,
CODE93 = 0x48,
CODE128 = 0x49,
GS1_128 = 0x4A,
GS1_DATABAR_OMNIDIRECTIONAL = 0x4B,
GS1_DATABAR_TRUNCATED = 0x4C,
GS1_DATABAR_LIMITED = 0x4D,
GS1_DATABAR_EXPANDED = 0x4E,
}
}

View File

@@ -0,0 +1,68 @@
namespace ESCPOS_NET.Emitters
{
#pragma warning disable SA1602 // Enumeration items should be documented
#pragma warning disable SA1600 // Elements should be documented
public enum CodePage
{
PC437_USA_STANDARD_EUROPE_DEFAULT = 0,
KATAKANA = 1,
PC850_MULTILINGUAL = 2,
PC860_PORTUGUESE = 3,
PC863_CANADIAN_FRENCH = 4,
PC865_NORDIC = 5,
HIRAGANA = 6,
ONE_PASS_KANJI = 7,
ONE_PASS_KANJI2 = 8,
PC851_GREEK = 11,
PC853_TURKISH = 12,
PC857_TURKISH = 13,
PC737_GREEK = 14,
ISO8859_7_GREEK = 15,
WPC1252 = 16,
PC866_CYRILLIC2 = 17,
PC852_LATIN2 = 18,
PC858_EURO = 19,
KU42_THAI = 20,
TIS11_THAI = 21,
TIS13_THAI = 22,
TIS14_THAI = 23,
TIS16_THAI = 24,
TIS17_THAI = 25,
TIS18_THAI = 26,
TCVN3_VIETNAMESE_L = 30,
TCVN3_VIETNAMESE_U = 31,
PC720_ARABIC = 32,
WPC775_BALTIC_RIM = 33,
PC855_CYRILLIC = 34,
PC861_ICELANDIC = 35,
PC862_HEBREW = 36,
PC864_ARABIC = 37,
PC869_GREEK = 38,
ISO8859_2_LATIN2 = 39,
ISO8859_15_LATIN9 = 40,
PC1098_FARSI = 41,
PC1118_LITHUANIAN = 42,
PC1119_LITHUANIAN = 43,
PC1125_UKRANIAN = 44,
WPC1250_LATIN2 = 45,
WPC1251_CYRILLIC = 46,
WPC1253_GREEK = 47,
WPC1254_TURKISH = 48,
WPC1255_HEBREW = 49,
WPC1256_ARABIC = 50,
WPC1257_BALTIC_RIM = 51,
WPC1258_VIETNAMESE = 52,
KZ1048_KAZAKHSTAN = 53,
DEVANAGARI = 66,
BENGALI = 67,
TAMIL = 68,
TELUGU = 69,
ASSAMESE = 70,
ORIYA = 71,
KANNADA = 72,
MALAYALAM = 73,
GUJARATI = 74,
PUNJABI = 75,
MARATHI = 82,
}
}

View File

@@ -0,0 +1,12 @@
using System;
namespace ESCPOS_NET.Emitters
{
[Flags]
[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 Color
{
Black = 0,
Red = 1
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace ESCPOS_NET.Emitters
{
[Flags]
[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 PrintStyle
{
None = 0,
FontB = 1,
Bold = 1 << 3,
DoubleHeight = 1 << 4,
DoubleWidth = 1 << 5,
Underline = 1 << 7,
}
}

View File

@@ -0,0 +1,92 @@
namespace ESCPOS_NET.Emitters
{
public interface ICommandEmitter
{
/* Print Commands */
byte[] PrintLine(string line = null);
byte[] Print(string line);
byte[] FeedLines(int lineCount);
byte[] FeedLinesReverse(int lineCount);
byte[] FeedDots(int dotCount);
/* Line Spacing Commands */
byte[] ResetLineSpacing();
byte[] SetLineSpacingInDots(int dots);
/* Operational Commands */
byte[] Initialize();
byte[] Enable();
byte[] Disable();
/* Cash Drawer Commands */
byte[] CashDrawerOpenPin2(int pulseOnTimeMs = 120, int pulseOffTimeMs = 240);
byte[] CashDrawerOpenPin5(int pulseOnTimeMs = 120, int pulseOffTimeMs = 240);
/* Character Commands */
byte[] SetStyles(PrintStyle style);
byte[] LeftAlign();
byte[] RightAlign();
byte[] CenterAlign();
byte[] ReverseMode(bool activate);
byte[] RightCharacterSpacing(int spaceCount);
byte[] UpsideDownMode(bool activate);
byte[] CodePage(CodePage codePage);
byte[] Color(Color color);
/* Action Commands */
byte[] FullCut();
byte[] PartialCut();
byte[] FullCutAfterFeed(int lineCount);
byte[] PartialCutAfterFeed(int lineCount);
/* Image Commands */
byte[] SetImageDensity(bool isHiDPI);
byte[] BufferImage(byte[] image, int maxWidth, bool isLegacy = false, int color = 1);
byte[] WriteImageFromBuffer();
byte[] PrintImage(byte[] image, bool isHiDPI, bool isLegacy = false, int maxWidth = -1, int color = 1);
/* Status Commands */
byte[] EnableAutomaticStatusBack();
byte[] EnableAutomaticInkStatusBack();
/* Barcode Commands */
byte[] PrintBarcode(BarcodeType type, string barcode, BarcodeCode code = BarcodeCode.CODE_B);
byte[] PrintQRCode(string data, TwoDimensionCodeType type = TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode size = Size2DCode.NORMAL, CorrectionLevel2DCode correction = CorrectionLevel2DCode.PERCENT_7);
byte[] Print2DCode(TwoDimensionCodeType type, string data, Size2DCode size = Size2DCode.NORMAL, CorrectionLevel2DCode correction = CorrectionLevel2DCode.PERCENT_7);
byte[] SetBarcodeHeightInDots(int height);
byte[] SetBarWidth(BarWidth width);
byte[] SetBarLabelPosition(BarLabelPrintPosition position);
byte[] SetBarLabelFontB(bool fontB);
/* 2D-Code Commands */
}
}

View File

@@ -0,0 +1,64 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp
{
public static class ImageSharpExtensions
{
public static byte[] ToSingleBitPixelByteArray(this Image<Rgba32> image, bool rasterFormat = true, int? maxWidth = null, int? maxHeight = null, float threshold = 0.5F)
{
image.Mutate(img =>
{
if (maxWidth.HasValue || maxHeight.HasValue)
{
img.Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(maxWidth ?? int.MaxValue, maxHeight ?? int.MaxValue),
});
}
img.BackgroundColor(Color.White);
img.Grayscale().BinaryDither(KnownDitherings.Stucki);
if (!rasterFormat)
{
img.Rotate(RotateMode.Rotate90);
img.Flip(FlipMode.Horizontal);
}
});
var result = image.ToSingleBitPixelByteArray();
return result;
}
private static byte[] ToSingleBitPixelByteArray(this Image<Rgba32> image)
{
var bytesPerRow = (image.Width + 7 & -8) / 8;
var result = new byte[bytesPerRow * image.Height];
for (int y = 0; y < image.Height; y++)
{
var row = image.GetPixelRowSpan(y);
var rowStartPosition = y * bytesPerRow;
for (int x = 0; x < row.Length; x++)
{
if (!row[x].IsBlack())
{
continue;
}
result[rowStartPosition + (x / 8)] |= (byte)(0x01 << (7 - (x % 8)));
}
}
return result;
}
private static bool IsBlack(this Rgba32 rgba)
{
return rgba.R == 0 && rgba.G == 0 && rgba.B == 0;
}
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace ESCPOS_NET.Extensions
{
public static class TaskExtensions
{
public static void LogExceptions(this Task task)
{
task.ContinueWith(t =>
{
var aggException = t.Exception.Flatten();
foreach (var exception in aggException.InnerExceptions)
{
Logging.Logger?.LogError(exception, "Uncaught Thread Exception.");
}
},
TaskContinuationOptions.OnlyOnFaulted);
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Collections.Generic;
namespace ESCPOS_NET.Extensions
{
public static class Util
{
/// <summary>
/// Adds to the list of T items and/or enumerables of T. All other types will be ignored and not added to the list.
/// </summary>
/// <typeparam name="T">List's items type.</typeparam>
/// <param name="list">List to be added the items.</param>
/// <param name="items">Items to be added.</param>
/// <returns>True if no item was ignored, otherwise False.</returns>
public static bool AddRange<T>(this List<T> list, params object[] items)
{
bool ignoredItems = false;
foreach (var item in items)
{
if (item is T itemT)
{
list.Add(itemT);
}
else if (item is IEnumerable<T> arrayT)
{
list.AddRange(arrayT);
}
else
{
ignoredItems = true;
}
}
return !ignoredItems;
}
}
}

View File

@@ -0,0 +1,9 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:Using directives should be placed correctly", Justification = "Placing usings within namespaces is not default in VS or in generated code.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Visual Studio by default recommends removing prefixed 'this'.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:Field names should not begin with underscore", Justification = "MS style guide uses _ for private members.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "<Pending>", Scope = "type", Target = "~T:ESCPOS_NET.Emitters.CodePage")]

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Luke Paireepinart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,19 @@
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace ESCPOS_NET
{
public static class Logging
{
public static ILogger Logger { get; set; }
static Logging()
{
TaskScheduler.UnobservedTaskException += (s, e) =>
{
//LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
e.SetObserved();
};
}
}
}

View File

@@ -0,0 +1,378 @@
using ESCPOS_NET.Emitters.BaseCommandValues;
using ESCPOS_NET.Utilities;
using ESCPOS_NET.Utils;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
namespace ESCPOS_NET
{
public abstract partial class BasePrinter : IPrinter, IDisposable
{
private bool disposed = false;
//private volatile bool _isMonitoring;
private CancellationTokenSource _readCancellationTokenSource;
private CancellationTokenSource _writeCancellationTokenSource;
private readonly int _maxBytesPerWrite = 15000; // max byte chunks to write at once.
public PrinterStatusEventArgs Status { get; private set; } = new PrinterStatusEventArgs();
public event EventHandler StatusChanged;
public event EventHandler Disconnected;
public event EventHandler Connected;
//public event EventHandler WriteFailed;
//public event EventHandler Idle;
protected BinaryWriter Writer { get; set; }
protected BinaryReader Reader { get; set; }
protected ConcurrentQueue<byte> ReadBuffer { get; set; } = new ConcurrentQueue<byte>();
protected ConcurrentQueue<byte[]> WriteBuffer { get; set; } = new ConcurrentQueue<byte[]>();
protected int BytesWrittenSinceLastFlush { get; set; } = 0;
protected volatile bool IsConnected = true;
public string PrinterName { get; protected set; }
protected BasePrinter()
{
PrinterName = Guid.NewGuid().ToString();
Init();
}
protected BasePrinter(string printerName)
{
if (string.IsNullOrEmpty(printerName))
{
printerName = Guid.NewGuid().ToString();
}
PrinterName = printerName;
Init();
}
private void Init()
{
_readCancellationTokenSource = new CancellationTokenSource();
_writeCancellationTokenSource = new CancellationTokenSource();
Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Initializing Task Threads...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
//Task.Factory.StartNew(MonitorPrinterStatusLongRunningTask, _connectivityCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
Task.Factory.StartNew(WriteLongRunningTask, _writeCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
Task.Factory.StartNew(ReadLongRunningTask, _readCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
// TODO: read and status monitoring probably won't work for fileprinter, should let printer types disable this feature.
Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Task Threads started", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
protected void InvokeConnect()
{
Task.Run(() => Connected?.Invoke(this, new ConnectionEventArgs() { IsConnected = true }));
}
protected void InvokeDisconnect()
{
Task.Run(() => Disconnected?.Invoke(this, new ConnectionEventArgs() { IsConnected = false }));
}
protected virtual void Reconnect()
{
// Implemented in the network printer
}
protected virtual async void WriteLongRunningTask()
{
while (true)
{
if (_writeCancellationTokenSource != null && _writeCancellationTokenSource.IsCancellationRequested)
{
Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Write Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
break;
}
await Task.Delay(100);
if (!IsConnected)
{
continue;
}
try
{
var didDequeue = WriteBuffer.TryDequeue(out var nextBytes);
if (didDequeue && nextBytes?.Length > 0)
{
WriteToBinaryWriter(nextBytes);
}
}
catch (IOException ex)
{
// Thrown if the printer times out the socket connection
// default is 90 seconds
//Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing IOException... sometimes happens with network printers. Should get reconnected automatically.");
}
catch (Exception ex)
{
// Swallow the exception
//Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing generic read exception... sometimes happens with serial port printers.");
}
}
}
protected virtual async void ReadLongRunningTask()
{
while (true)
{
if (_readCancellationTokenSource != null && _readCancellationTokenSource.IsCancellationRequested)
{
Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Read Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
break;
}
await Task.Delay(100);
if (Reader == null) continue;
if (!IsConnected) continue;
try
{
// Sometimes the serial port lib will throw an exception and read past the end of the queue if a
// status changes while data is being written. We just ignore these bytes.
var b = Reader.BaseStream.ReadByte();
if (b >= 0 && b <= 255)
{
ReadBuffer.Enqueue((byte)b);
DataAvailable();
}
}
catch (Exception ex)
{
// Swallow the exception
//Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing generic read exception... sometimes happens with serial port printers.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
}
}
public virtual void Write(params byte[][] arrays)
{
Write(ByteSplicer.Combine(arrays));
}
public virtual void Write(byte[] bytes)
{
WriteBuffer.Enqueue(bytes);
}
protected virtual void WriteToBinaryWriter(byte[] bytes)
{
if (!IsConnected)
{
Logging.Logger?.LogInformation("[{Function}]:[{PrinterName}] Attempted to write but printer isn't connected. Attempting to reconnect...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
Reconnect();
}
if (!IsConnected)
{
Logging.Logger?.LogError("[{Function}]:[{PrinterName}] Unrecoverable connectivity error writing to printer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
throw new IOException("Unrecoverable connectivity error writing to printer.");
}
int bytePointer = 0;
int bytesLeft = bytes.Length;
bool hasFlushed = false;
while (bytesLeft > 0)
{
int count = Math.Min(_maxBytesPerWrite, bytesLeft);
try
{
Writer.Write(bytes, bytePointer, count);
}
catch (IOException e)
{
Reconnect();
if (!IsConnected)
{
Logging.Logger?.LogError(e, "[{Function}]:[{PrinterName}] Unrecoverable connectivity error writing to printer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
Writer.Write(bytes, bytePointer, count);
}
BytesWrittenSinceLastFlush += count;
if (BytesWrittenSinceLastFlush >= 200)
{
// Immediately trigger a flush before proceeding so the output buffer will not be delayed.
hasFlushed = true;
Flush(null, null);
}
bytePointer += count;
bytesLeft -= count;
}
if (!hasFlushed)
{
Task.Run(async () => { await Task.Delay(50); Flush(null, null); });
}
}
protected virtual void Flush(object sender, ElapsedEventArgs e)
{
try
{
BytesWrittenSinceLastFlush = 0;
Writer.Flush();
}
catch (Exception ex)
{
Logging.Logger?.LogError(ex, "[{Function}]:[{PrinterName}] Flush threw exception.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
}
public virtual void DataAvailable()
{
if (ReadBuffer.Count() % 4 == 0)
{
var bytes = new byte[4];
for (int i = 0; i < 4; i++)
{
if (!ReadBuffer.TryDequeue(out bytes[i]))
{
// Ran out of bytes unexpectedly.
return;
}
}
TryUpdatePrinterStatus(bytes);
// TODO: call other update handlers.
}
}
private void TryUpdatePrinterStatus(byte[] bytes)
{
var bytesToString = BitConverter.ToString(bytes);
Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] TryUpdatePrinterStatus: Received flag values {bytesToString}", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName, bytesToString);
// Check header bits 0, 1 and 7 are 0, and 4 is 1
if (bytes[0].IsBitNotSet(0) && bytes[0].IsBitNotSet(1) && bytes[0].IsBitSet(4) && bytes[0].IsBitNotSet(7))
{
Status = new PrinterStatusEventArgs()
{
// byte[0] == 20 cash drawer closed
// byte[0] == 16 cash drawer open
// Note some cash drawers do not close properly.
IsCashDrawerOpen = bytes[0].IsBitNotSet(2),
IsPrinterOnline = bytes[0].IsBitNotSet(3),
IsCoverOpen = bytes[0].IsBitSet(5),
IsPaperCurrentlyFeeding = bytes[0].IsBitSet(6),
IsWaitingForOnlineRecovery = bytes[1].IsBitSet(0),
IsPaperFeedButtonPushed = bytes[1].IsBitSet(1),
DidRecoverableNonAutocutterErrorOccur = bytes[1].IsBitSet(2),
DidAutocutterErrorOccur = bytes[1].IsBitSet(3),
DidUnrecoverableErrorOccur = bytes[1].IsBitSet(5),
DidRecoverableErrorOccur = bytes[1].IsBitSet(6),
IsPaperLow = bytes[2].IsBitSet(0) && bytes[2].IsBitSet(1),
IsPaperOut = bytes[2].IsBitSet(2) && bytes[2].IsBitSet(3),
};
}
if (StatusChanged != null)
{
Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Invoking Status Changed Event Handler...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
StatusChanged?.Invoke(this, Status);
}
}
~BasePrinter()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void OverridableDispose() // This method should only be called by the Dispose method. // It allows synchronous disposing of derived class dependencies with base class disposes.
{
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
try
{
_readCancellationTokenSource?.Cancel();
}
catch (Exception e)
{
Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue during cancellation token cancellation call.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
try
{
Reader?.Close();
}
catch (Exception e)
{
Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue closing reader.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
try
{
Reader?.Dispose();
}
catch (Exception e)
{
Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue disposing reader.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
try
{
Writer?.Close();
}
catch (Exception e)
{
Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue closing writer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
try
{
Writer?.Dispose();
}
catch (Exception e)
{
Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue disposing writer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
try
{
OverridableDispose();
}
catch (Exception e)
{
Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue during overridable dispose.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName);
}
}
disposed = true;
}
public PrinterStatusEventArgs GetStatus()
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace ESCPOS_NET
{
public class ConnectionEventArgs : EventArgs
{
public bool IsConnected;
}
}

View File

@@ -0,0 +1,36 @@
using System.IO;
namespace ESCPOS_NET
{
public class FilePrinter : BasePrinter
{
private readonly FileStream _file;
// TODO: default values to their default values in ctor.
public FilePrinter(string filePath, bool createIfNotExists = false)
: base()
{
if (createIfNotExists)
{
_file = File.Open(filePath, FileMode.OpenOrCreate);
}
else
{
_file = File.Open(filePath, FileMode.Open);
}
Writer = new BinaryWriter(_file);
Reader = new BinaryReader(_file);
}
~FilePrinter()
{
Dispose(false);
}
protected override void OverridableDispose()
{
_file?.Close();
_file?.Dispose();
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace ESCPOS_NET
{
public interface IPrinter
{
PrinterStatusEventArgs GetStatus();
void Write(params byte[][] arrays);
void Write(byte[] bytes);
event EventHandler StatusChanged;
event EventHandler Disconnected;
event EventHandler Connected;
//event EventHandler WriteFailed;
//event EventHandler Idle;
//event EventHandler IdleDisconnected; is this useful? to know that it disconnected because of idle? probably better to have this as info in disconnected event object instead.
}
}

Some files were not shown because too many files have changed in this diff Show More