Print problemer
Har muligvis rettet den print fejl der er - men mangler at få det testet. Der er slettet en masse projekter som ikke bliver brugt mere. Og flyttet rundt på de få projekter som er tilbage. Dette er V2 og i sit eget repo, så dette er initial commit
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
9
PointOfSale/EpsonPrinterLinux/Enums/AlignmentEnum.cs
Normal file
9
PointOfSale/EpsonPrinterLinux/Enums/AlignmentEnum.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace EpsonPrinter.Enums
|
||||
{
|
||||
public enum AlignmentEnum
|
||||
{
|
||||
Left,
|
||||
Center,
|
||||
Right
|
||||
}
|
||||
}
|
||||
12
PointOfSale/EpsonPrinterLinux/Enums/PrintStylesEnum.cs
Normal file
12
PointOfSale/EpsonPrinterLinux/Enums/PrintStylesEnum.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace EpsonPrinter.Enums
|
||||
{
|
||||
public enum PrintStylesEnum
|
||||
{
|
||||
None,
|
||||
FontB,
|
||||
Bold,
|
||||
DoubleHeight,
|
||||
DoubleWidth,
|
||||
UnderLine
|
||||
}
|
||||
}
|
||||
23
PointOfSale/EpsonPrinterLinux/EpsonPrinter.csproj
Normal file
23
PointOfSale/EpsonPrinterLinux/EpsonPrinter.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerfileContext>..\Pos</DockerfileContext>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="ESC-POS\**" />
|
||||
<Content Remove="ESC-POS\**" />
|
||||
<EmbeddedResource Remove="ESC-POS\**" />
|
||||
<None Remove="ESC-POS\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ESCPOS_NET" Version="3.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
18
PointOfSale/EpsonPrinterLinux/Model/BodyModel.cs
Normal file
18
PointOfSale/EpsonPrinterLinux/Model/BodyModel.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EpsonPrinter.Model
|
||||
{
|
||||
public class BodyModel
|
||||
{
|
||||
public List<BodyProductModel> Products { get; set; }
|
||||
public decimal TotalPrice { get; set; }
|
||||
public decimal TotalVat { get; set; }
|
||||
public int ReceiptNumber { get; set; }
|
||||
public string ReceiptTime { get; set; }
|
||||
public string Staff { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
15
PointOfSale/EpsonPrinterLinux/Model/BodyProductModel.cs
Normal file
15
PointOfSale/EpsonPrinterLinux/Model/BodyProductModel.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EpsonPrinter.Model
|
||||
{
|
||||
public class BodyProductModel
|
||||
{
|
||||
public string Product { get; set; }
|
||||
public int NoOfProduct { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public decimal TotalPrice { get; set; }
|
||||
}
|
||||
}
|
||||
16
PointOfSale/EpsonPrinterLinux/Model/FooterModel.cs
Normal file
16
PointOfSale/EpsonPrinterLinux/Model/FooterModel.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EpsonPrinter.Enums;
|
||||
|
||||
namespace EpsonPrinter.Model
|
||||
{
|
||||
public class FooterModel
|
||||
{
|
||||
public string Value { get; set; }
|
||||
public PrintStyleModel PrintStyles { get; set; }
|
||||
public AlignmentEnum TextAlignment { get; set; }
|
||||
public int FeedingLines { get; set; }
|
||||
}
|
||||
}
|
||||
16
PointOfSale/EpsonPrinterLinux/Model/HeaderModel.cs
Normal file
16
PointOfSale/EpsonPrinterLinux/Model/HeaderModel.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EpsonPrinter.Enums;
|
||||
|
||||
namespace EpsonPrinter.Model
|
||||
{
|
||||
public class HeaderModel
|
||||
{
|
||||
public string Value { get; set; }
|
||||
public PrintStyleModel PrintStyles { get; set; }
|
||||
public AlignmentEnum TextAlignment { get; set; }
|
||||
public int FeedingLines { get; set; }
|
||||
}
|
||||
}
|
||||
16
PointOfSale/EpsonPrinterLinux/Model/PrintStyleModel.cs
Normal file
16
PointOfSale/EpsonPrinterLinux/Model/PrintStyleModel.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EpsonPrinter.Model
|
||||
{
|
||||
public class PrintStyleModel
|
||||
{
|
||||
public bool FontB { get; set; }
|
||||
public bool Bold { get; set; }
|
||||
public bool DoubleHeight { get; set; }
|
||||
public bool DoubleWidth { get; set; }
|
||||
public bool Underline { get; set; }
|
||||
}
|
||||
}
|
||||
13
PointOfSale/EpsonPrinterLinux/Model/ReceiptModel.cs
Normal file
13
PointOfSale/EpsonPrinterLinux/Model/ReceiptModel.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EpsonPrinter.Model
|
||||
{
|
||||
public class ReceiptModel
|
||||
{
|
||||
public string LogoBase64 { get; set; }
|
||||
public List<HeaderModel> Header { get; set; }
|
||||
public BodyModel BodyModel { get; set; }
|
||||
public List<FooterModel> Footer { get; set; }
|
||||
}
|
||||
}
|
||||
10
PointOfSale/EpsonPrinterLinux/Model/SaleOfDayDetail.cs
Normal file
10
PointOfSale/EpsonPrinterLinux/Model/SaleOfDayDetail.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace EpsonPrinter.Model
|
||||
{
|
||||
public class SaleOfDayDetail
|
||||
{
|
||||
public string Category { get; set; }
|
||||
public decimal TotalSale { get; set; }
|
||||
}
|
||||
}
|
||||
14
PointOfSale/EpsonPrinterLinux/Model/SaleOfDayModel.cs
Normal file
14
PointOfSale/EpsonPrinterLinux/Model/SaleOfDayModel.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace EpsonPrinter.Model
|
||||
{
|
||||
public class SaleOfDayModel
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
public int TotalCustomers { get; set; }
|
||||
public Decimal TotalSale { get; set; }
|
||||
public List<SaleOfDayDetail> SaleOfDayDetail { get; set; } = new();
|
||||
|
||||
}
|
||||
}
|
||||
23
PointOfSale/EpsonPrinterLinux/Program.cs
Normal file
23
PointOfSale/EpsonPrinterLinux/Program.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using EpsonPrinter.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Services
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
builder.Services.AddScoped<EpsonPrintService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Swagger (altid aktiv – vi er på en Pi, ikke i produktion)
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
// Routing
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
31
PointOfSale/EpsonPrinterLinux/Properties/launchSettings.json
Normal file
31
PointOfSale/EpsonPrinterLinux/Properties/launchSettings.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:23653",
|
||||
"sslPort": 44334
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"EpsonPrinter": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": "true",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
PointOfSale/EpsonPrinterLinux/README_LINUX_USB.txt
Normal file
18
PointOfSale/EpsonPrinterLinux/README_LINUX_USB.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Linux USB version of the EpsonPrinter API.
|
||||
|
||||
What changed:
|
||||
- Keeps the incoming JSON models for /api/PosPrinter/Receipt and /api/PosPrinter/SaleOfDay
|
||||
- Replaces COM/SerialPrinter transport with raw writes to Linux USB printer device
|
||||
- Uses /dev/usb/lp0 by default from appsettings.json
|
||||
- Builds a standard receipt when parts of the payload are missing
|
||||
- Pulls fallback values from appsettings.json under PrintSettings:Defaults
|
||||
|
||||
Typical run:
|
||||
dotnet restore
|
||||
dotnet run
|
||||
|
||||
Typical endpoint:
|
||||
POST /api/PosPrinter/Receipt
|
||||
|
||||
Important setting:
|
||||
PrintSettings:DevicePath = /dev/usb/lp0
|
||||
306
PointOfSale/EpsonPrinterLinux/Services/EpsonPrintService.cs
Normal file
306
PointOfSale/EpsonPrinterLinux/Services/EpsonPrintService.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using EpsonPrinter.Model;
|
||||
using ESCPOS_NET;
|
||||
using ESCPOS_NET.Emitters;
|
||||
using ESCPOS_NET.Utilities;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace EpsonPrinter.Services
|
||||
{
|
||||
public class EpsonPrintService
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
private readonly string _devicePath;
|
||||
private readonly string? _previewPath;
|
||||
private readonly bool _isDevelopment;
|
||||
|
||||
public EpsonPrintService(IConfiguration config, IWebHostEnvironment environment)
|
||||
{
|
||||
_config = config;
|
||||
_isDevelopment = environment.IsDevelopment();
|
||||
|
||||
var configuredPath = _config["PrintSettings:DevicePath"] ?? "/dev/usb/lp0";
|
||||
_devicePath = _isDevelopment && !Path.IsPathRooted(configuredPath)
|
||||
? Path.Combine(environment.ContentRootPath, configuredPath)
|
||||
: configuredPath;
|
||||
|
||||
var configuredPreviewPath = _config["PrintSettings:PreviewPath"] ?? "printer-preview.txt";
|
||||
_previewPath = _isDevelopment
|
||||
? Path.IsPathRooted(configuredPreviewPath)
|
||||
? configuredPreviewPath
|
||||
: Path.Combine(environment.ContentRootPath, configuredPreviewPath)
|
||||
: null;
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
}
|
||||
|
||||
public void PrintReceipt(ReceiptModel model)
|
||||
{
|
||||
ICommandEmitter e = new EPSON();
|
||||
InitAndWrite(e);
|
||||
|
||||
List<byte[][]> printBytes = new List<byte[][]>();
|
||||
PrintHeader printHeader = new PrintHeader(e);
|
||||
PrintBody printBody = new PrintBody(e, _config);
|
||||
PrintFooter printFooter = new PrintFooter(e);
|
||||
|
||||
var header = (model.Header != null && model.Header.Any()) ? model.Header : BuildDefaultHeader();
|
||||
var body = model.BodyModel ?? BuildDefaultBody();
|
||||
if (string.IsNullOrWhiteSpace(body.Staff))
|
||||
{
|
||||
body.Staff = _config["PrintSettings:Defaults:Staff"] ?? "Ukendt";
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(body.ReceiptTime))
|
||||
{
|
||||
body.ReceiptTime = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss");
|
||||
}
|
||||
if (body.ReceiptNumber <= 0)
|
||||
{
|
||||
body.ReceiptNumber = Math.Abs(Environment.TickCount % 1000000);
|
||||
}
|
||||
body.Products ??= new List<BodyProductModel>();
|
||||
if (!body.Products.Any())
|
||||
{
|
||||
body.Products.Add(new BodyProductModel
|
||||
{
|
||||
Product = "Standard vare",
|
||||
NoOfProduct = 1,
|
||||
Price = 99.00m,
|
||||
TotalPrice = 99.00m
|
||||
});
|
||||
}
|
||||
if (body.TotalPrice <= 0)
|
||||
{
|
||||
body.TotalPrice = body.Products.Sum(x => x.TotalPrice <= 0 ? x.Price * x.NoOfProduct : x.TotalPrice);
|
||||
}
|
||||
if (body.TotalVat <= 0)
|
||||
{
|
||||
body.TotalVat = Math.Round(body.TotalPrice * 0.25m, 2);
|
||||
}
|
||||
|
||||
var footer = (model.Footer != null && model.Footer.Any()) ? model.Footer : BuildDefaultFooter();
|
||||
|
||||
printBytes.AddRange(printHeader.Print(header));
|
||||
printBytes.AddRange(printBody.Print(body));
|
||||
printBytes.AddRange(printFooter.Print(footer));
|
||||
|
||||
byte[][] array = printBytes.SelectMany(c => c).ToArray();
|
||||
Write(array);
|
||||
Write(e.FeedLines(5));
|
||||
Write(e.FullCut());
|
||||
}
|
||||
|
||||
public void PrintSaleOfDay(SaleOfDayModel model)
|
||||
{
|
||||
ICommandEmitter e = new EPSON();
|
||||
InitAndWrite(e);
|
||||
PrintSaleOfDay(model, e);
|
||||
}
|
||||
|
||||
private void InitAndWrite(ICommandEmitter e)
|
||||
{
|
||||
Write(e.Initialize());
|
||||
Write(e.Enable());
|
||||
Write(e.CodePage(CodePage.ISO8859_15_LATIN9));
|
||||
Write(e.EnableAutomaticStatusBack());
|
||||
}
|
||||
|
||||
private void PrintSaleOfDay(SaleOfDayModel model, ICommandEmitter e)
|
||||
{
|
||||
List<byte[][]> data = new List<byte[][]>();
|
||||
|
||||
byte[][] dateBytes =
|
||||
{
|
||||
e.SetStyles(PrintStyle.DoubleWidth),
|
||||
e.LeftAlign(),
|
||||
e.PrintLine($"Dato: {model.Date:dd-MM-yyyy}")
|
||||
};
|
||||
data.Add(dateBytes);
|
||||
|
||||
byte[][] seperater =
|
||||
{
|
||||
e.SetStyles(PrintStyle.None),
|
||||
e.LeftAlign(),
|
||||
e.PrintLine("-----------------------------------")
|
||||
};
|
||||
data.Add(seperater);
|
||||
|
||||
foreach (SaleOfDayDetail saleOfDayDetail in model.SaleOfDayDetail)
|
||||
{
|
||||
string pS = $"{saleOfDayDetail.Category} : {saleOfDayDetail.TotalSale.ToString("#.#0", CultureInfo.InvariantCulture)}";
|
||||
byte[][] totalPrice =
|
||||
{
|
||||
e.SetStyles(PrintStyle.DoubleWidth),
|
||||
e.LeftAlign(),
|
||||
e.PrintLine(pS)
|
||||
};
|
||||
data.Add(totalPrice);
|
||||
}
|
||||
|
||||
data.Add(seperater);
|
||||
|
||||
string totalSale = $"Total salg: {model.TotalSale.ToString("#.#0", CultureInfo.InvariantCulture)}";
|
||||
byte[][] tSBytes =
|
||||
{
|
||||
e.SetStyles(PrintStyle.DoubleWidth),
|
||||
e.LeftAlign(),
|
||||
e.PrintLine(totalSale)
|
||||
};
|
||||
data.Add(tSBytes);
|
||||
|
||||
string totalCustomer = $"Antal kunder: {model.TotalCustomers}";
|
||||
byte[][] tCBytes =
|
||||
{
|
||||
e.SetStyles(PrintStyle.DoubleWidth),
|
||||
e.LeftAlign(),
|
||||
e.PrintLine(totalCustomer)
|
||||
};
|
||||
data.Add(tCBytes);
|
||||
|
||||
byte[][] array = data.SelectMany(c => c).ToArray();
|
||||
Write(array);
|
||||
Write(e.FeedLines(5));
|
||||
Write(e.FullCut());
|
||||
}
|
||||
|
||||
private List<HeaderModel> BuildDefaultHeader()
|
||||
{
|
||||
return new List<HeaderModel>
|
||||
{
|
||||
new HeaderModel
|
||||
{
|
||||
Value = _config["PrintSettings:Defaults:StoreName"] ?? "TableTop3D",
|
||||
TextAlignment = Enums.AlignmentEnum.Center,
|
||||
FeedingLines = 0,
|
||||
PrintStyles = new PrintStyleModel { Bold = true, DoubleWidth = true, DoubleHeight = true }
|
||||
},
|
||||
new HeaderModel
|
||||
{
|
||||
Value = _config["PrintSettings:Defaults:AddressLine1"] ?? string.Empty,
|
||||
TextAlignment = Enums.AlignmentEnum.Center,
|
||||
FeedingLines = 0,
|
||||
PrintStyles = new PrintStyleModel { Bold = true }
|
||||
},
|
||||
new HeaderModel
|
||||
{
|
||||
Value = _config["PrintSettings:Defaults:AddressLine2"] ?? string.Empty,
|
||||
TextAlignment = Enums.AlignmentEnum.Center,
|
||||
FeedingLines = 0,
|
||||
PrintStyles = new PrintStyleModel()
|
||||
},
|
||||
new HeaderModel
|
||||
{
|
||||
Value = _config["PrintSettings:Defaults:Cvr"] ?? string.Empty,
|
||||
TextAlignment = Enums.AlignmentEnum.Center,
|
||||
FeedingLines = 1,
|
||||
PrintStyles = new PrintStyleModel()
|
||||
}
|
||||
}.Where(x => !string.IsNullOrWhiteSpace(x.Value)).ToList();
|
||||
}
|
||||
|
||||
private FooterModel CreateFooterLine(string value, bool bold = false)
|
||||
{
|
||||
return new FooterModel
|
||||
{
|
||||
Value = value,
|
||||
TextAlignment = Enums.AlignmentEnum.Center,
|
||||
FeedingLines = 0,
|
||||
PrintStyles = new PrintStyleModel { Bold = bold }
|
||||
};
|
||||
}
|
||||
|
||||
private List<FooterModel> BuildDefaultFooter()
|
||||
{
|
||||
var values = new[]
|
||||
{
|
||||
_config["PrintSettings:Defaults:FooterLine1"] ?? "Tak for dit køb!",
|
||||
_config["PrintSettings:Defaults:FooterLine2"] ?? string.Empty,
|
||||
_config["PrintSettings:Defaults:Phone"] ?? string.Empty
|
||||
}.Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
|
||||
|
||||
return values.Select((x, i) => CreateFooterLine(x, i == 0)).ToList();
|
||||
}
|
||||
|
||||
private BodyModel BuildDefaultBody()
|
||||
{
|
||||
return new BodyModel
|
||||
{
|
||||
ReceiptNumber = Math.Abs(Environment.TickCount % 1000000),
|
||||
ReceiptTime = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss"),
|
||||
Staff = _config["PrintSettings:Defaults:Staff"] ?? "Ukendt",
|
||||
Products = new List<BodyProductModel>
|
||||
{
|
||||
new BodyProductModel
|
||||
{
|
||||
Product = "Standard vare",
|
||||
NoOfProduct = 1,
|
||||
Price = 99.00m,
|
||||
TotalPrice = 99.00m
|
||||
}
|
||||
},
|
||||
TotalPrice = 99.00m,
|
||||
TotalVat = 24.75m
|
||||
};
|
||||
}
|
||||
|
||||
private void Write(byte[] data)
|
||||
{
|
||||
var fileMode = _isDevelopment ? FileMode.Append : FileMode.Open;
|
||||
using var stream = new FileStream(_devicePath, fileMode, FileAccess.Write, FileShare.ReadWrite);
|
||||
stream.Write(data, 0, data.Length);
|
||||
stream.Flush();
|
||||
|
||||
if (_previewPath != null)
|
||||
{
|
||||
AppendPreview(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendPreview(byte[] data)
|
||||
{
|
||||
using var writer = new StreamWriter(_previewPath!, append: true, Encoding.UTF8);
|
||||
var textEncoding = Encoding.GetEncoding("iso-8859-15");
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
byte value = data[i];
|
||||
if (value == 0x1B)
|
||||
{
|
||||
i += i + 1 < data.Length && data[i + 1] == 0x40 ? 1 : 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value == 0x1D)
|
||||
{
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value == 0x0A)
|
||||
{
|
||||
writer.WriteLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value >= 0x20)
|
||||
{
|
||||
writer.Write(textEncoding.GetString(new[] { value }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Write(byte[][] data)
|
||||
{
|
||||
foreach (var segment in data)
|
||||
{
|
||||
Write(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
PointOfSale/EpsonPrinterLinux/Services/PrintAlignment.cs
Normal file
24
PointOfSale/EpsonPrinterLinux/Services/PrintAlignment.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using EpsonPrinter.Enums;
|
||||
using ESCPOS_NET.Emitters;
|
||||
|
||||
namespace EpsonPrinter.Services
|
||||
{
|
||||
public class PrintAlignment
|
||||
{
|
||||
public byte[] GetTextAlignment(ICommandEmitter e, AlignmentEnum alignment)
|
||||
{
|
||||
if (alignment == AlignmentEnum.Left)
|
||||
return e.LeftAlign();
|
||||
|
||||
if (alignment == AlignmentEnum.Right)
|
||||
return e.RightAlign();
|
||||
|
||||
return e.CenterAlign();
|
||||
}
|
||||
}
|
||||
}
|
||||
117
PointOfSale/EpsonPrinterLinux/Services/PrintBody.cs
Normal file
117
PointOfSale/EpsonPrinterLinux/Services/PrintBody.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EpsonPrinter.Model;
|
||||
using ESCPOS_NET.Emitters;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace EpsonPrinter.Services
|
||||
{
|
||||
public class PrintBody
|
||||
{
|
||||
private ICommandEmitter _e;
|
||||
private IConfiguration _config;
|
||||
|
||||
private int productMaxWidth;
|
||||
|
||||
public PrintBody(ICommandEmitter e, IConfiguration config)
|
||||
{
|
||||
_e = e;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public List<byte[][]> Print(BodyModel model)
|
||||
{
|
||||
List<byte[][]> data = new List<byte[][]>();
|
||||
|
||||
byte[][] headerModel =
|
||||
{
|
||||
_e.SetStyles(PrintStyle.None),
|
||||
_e.LeftAlign(),
|
||||
_e.PrintLine($"Dato.........: {model.ReceiptTime}"),
|
||||
_e.PrintLine($"Ekspedient...: {model.Staff}"),
|
||||
_e.PrintLine($"Kvittering...: {model.ReceiptNumber}"),
|
||||
_e.FeedLines(1)
|
||||
};
|
||||
data.Add(headerModel);
|
||||
|
||||
int printWidth = Convert.ToInt32(_config["PrintSettings:PrintWidth"]);
|
||||
foreach (BodyProductModel product in model.Products)
|
||||
{
|
||||
//Size of product is max half of recipt
|
||||
string totalProductLine = MakeProductNameString(product.Product, printWidth);
|
||||
totalProductLine += MakeProductNumber(product.NoOfProduct, product.Price);
|
||||
totalProductLine = MakeTotalProductPrice(totalProductLine, product.TotalPrice,printWidth);
|
||||
|
||||
byte[][] p =
|
||||
{
|
||||
_e.PrintLine(totalProductLine)
|
||||
};
|
||||
data.Add(p);
|
||||
}
|
||||
|
||||
PrintString printString = new PrintString();
|
||||
string totalPricePrint = printString.MakePrintString(printWidth, $"Total: ", $"{model.TotalPrice:F} DKK");
|
||||
byte[][] totalPrice =
|
||||
{
|
||||
_e.FeedLines(1),
|
||||
_e.SetStyles(PrintStyle.Bold | PrintStyle.DoubleHeight),
|
||||
_e.RightAlign(),
|
||||
_e.PrintLine(totalPricePrint)
|
||||
};
|
||||
data.Add(totalPrice);
|
||||
|
||||
byte[][] vat =
|
||||
{
|
||||
_e.FeedLines(1),
|
||||
_e.SetStyles(PrintStyle.None),
|
||||
_e.LeftAlign(),
|
||||
_e.PrintLine(MakeVat(printWidth,model.TotalVat))
|
||||
};
|
||||
data.Add(vat);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private string MakeProductNameString(string product, int totalWidth)
|
||||
{
|
||||
string productString = product;
|
||||
productMaxWidth = (totalWidth / 2) - 5;
|
||||
int productLength = product.Length;
|
||||
if (productLength < productMaxWidth)
|
||||
{
|
||||
|
||||
for (int i = productLength; i < productMaxWidth; i++)
|
||||
{
|
||||
productString += ".";
|
||||
}
|
||||
}
|
||||
|
||||
return productString;
|
||||
}
|
||||
|
||||
private string MakeProductNumber(int noOfProduct, decimal price)
|
||||
{
|
||||
return $"{noOfProduct} á {price:F} DKK :";
|
||||
}
|
||||
|
||||
private string MakeTotalProductPrice(string buffer, decimal totalPrice, int printWidth)
|
||||
{
|
||||
PrintString printString = new PrintString();
|
||||
string p = $"{totalPrice:F} DKK";
|
||||
string makePrintString = printString.MakePrintString(printWidth, buffer, p);
|
||||
return makePrintString;
|
||||
}
|
||||
|
||||
private string MakeVat(int printWidth, decimal vat)
|
||||
{
|
||||
PrintString printString = new PrintString();
|
||||
string vatDesc = $"Moms udgør 25% :";
|
||||
string v = $"{vat:F} DKK";
|
||||
string vatString = printString.MakePrintString(printWidth, vatDesc, v);
|
||||
return vatString;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
PointOfSale/EpsonPrinterLinux/Services/PrintFooter.cs
Normal file
47
PointOfSale/EpsonPrinterLinux/Services/PrintFooter.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EpsonPrinter.Model;
|
||||
using ESCPOS_NET;
|
||||
using ESCPOS_NET.Emitters;
|
||||
|
||||
namespace EpsonPrinter.Services
|
||||
{
|
||||
public class PrintFooter
|
||||
{
|
||||
private ICommandEmitter _e;
|
||||
|
||||
public PrintFooter(ICommandEmitter e)
|
||||
{
|
||||
_e = e;
|
||||
}
|
||||
|
||||
public List<byte[][]> Print(List<FooterModel> model)
|
||||
{
|
||||
List<byte[][]> data = new List<byte[][]>();
|
||||
PrintStyleCombination printStyleCombination = new PrintStyleCombination();
|
||||
PrintAlignment printAlignment = new PrintAlignment();
|
||||
byte[][] feedLines =
|
||||
{
|
||||
_e.FeedLines(1)
|
||||
};
|
||||
data.Add(feedLines);
|
||||
foreach (FooterModel footerModel in model)
|
||||
{
|
||||
|
||||
PrintStyle printStyle = printStyleCombination.Combine(footerModel.PrintStyles);
|
||||
byte[][] bytes = {
|
||||
_e.SetStyles(PrintStyle.None),
|
||||
printAlignment.GetTextAlignment(_e,footerModel.TextAlignment),
|
||||
_e.SetStyles(printStyle),
|
||||
_e.PrintLine(footerModel.Value),
|
||||
_e.FeedLines(footerModel.FeedingLines)
|
||||
};
|
||||
data.Add(bytes);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
PointOfSale/EpsonPrinterLinux/Services/PrintHeader.cs
Normal file
47
PointOfSale/EpsonPrinterLinux/Services/PrintHeader.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using EpsonPrinter.Model;
|
||||
using ESCPOS_NET;
|
||||
using ESCPOS_NET.Emitters;
|
||||
using ESCPOS_NET.Utilities;
|
||||
|
||||
namespace EpsonPrinter.Services
|
||||
{
|
||||
public class PrintHeader
|
||||
{
|
||||
private ICommandEmitter _e;
|
||||
|
||||
public PrintHeader(ICommandEmitter e)
|
||||
{
|
||||
_e = e;
|
||||
}
|
||||
|
||||
public List<Byte[][]> Print(List<HeaderModel> model)
|
||||
{
|
||||
List<byte[][]> data = new List<byte[][]>();
|
||||
PrintStyleCombination printStyleCombination = new PrintStyleCombination();
|
||||
PrintAlignment printAlignment = new PrintAlignment();
|
||||
foreach (HeaderModel headerModel in model)
|
||||
{
|
||||
|
||||
PrintStyle printStyle = printStyleCombination.Combine(headerModel.PrintStyles);
|
||||
byte[][] bytes = {
|
||||
_e.SetStyles(PrintStyle.None),
|
||||
printAlignment.GetTextAlignment(_e,headerModel.TextAlignment),
|
||||
_e.SetStyles(printStyle),
|
||||
_e.PrintLine(headerModel.Value),
|
||||
_e.FeedLines(headerModel.FeedingLines)
|
||||
};
|
||||
data.Add(bytes);
|
||||
}
|
||||
byte[][] feedLines =
|
||||
{
|
||||
_e.FeedLines(1)
|
||||
};
|
||||
data.Add(feedLines);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
PointOfSale/EpsonPrinterLinux/Services/PrintString.cs
Normal file
37
PointOfSale/EpsonPrinterLinux/Services/PrintString.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EpsonPrinter.Services
|
||||
{
|
||||
public class PrintString
|
||||
{
|
||||
/// <summary>
|
||||
/// An appropriate interval is converted into the length of
|
||||
/// the tab about two texts. And make a printing data.
|
||||
/// </summary>
|
||||
/// <param name="iLineChars">
|
||||
/// The width of the territory which it prints on is converted into the number of
|
||||
/// characters, and that value is specified.
|
||||
/// </param>
|
||||
/// <param name="strBuf">
|
||||
/// It is necessary as an information for deciding the interval of the text.
|
||||
/// </param>
|
||||
/// <param name="strPrice">
|
||||
/// It is necessary as an information for deciding the interval of the text, too.
|
||||
/// </param>
|
||||
/// <returns>printing data.
|
||||
/// </returns>
|
||||
public String MakePrintString(int iLineChars, String strBuf, String strPrice)
|
||||
{
|
||||
int bufferWidth = Math.Max(0, iLineChars - strPrice.Length);
|
||||
if (strBuf.Length > bufferWidth)
|
||||
{
|
||||
strBuf = strBuf.Substring(0, bufferWidth);
|
||||
}
|
||||
|
||||
return strBuf.PadRight(bufferWidth) + strPrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EpsonPrinter.Enums;
|
||||
using EpsonPrinter.Model;
|
||||
using ESCPOS_NET.Emitters;
|
||||
|
||||
namespace EpsonPrinter.Services
|
||||
{
|
||||
public class PrintStyleCombination
|
||||
{
|
||||
public PrintStyle Combine(PrintStyleModel printStyleModel)
|
||||
{
|
||||
|
||||
//Bold
|
||||
if (printStyleModel.Bold && !printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.Bold;
|
||||
|
||||
if (printStyleModel.Bold && printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.Bold | PrintStyle.DoubleHeight;
|
||||
|
||||
if(printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth;
|
||||
|
||||
if (printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB;
|
||||
|
||||
if (printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline)
|
||||
return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline;
|
||||
|
||||
//DoubleHeight
|
||||
if (printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.DoubleHeight;
|
||||
|
||||
if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth;
|
||||
|
||||
if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB;
|
||||
|
||||
if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline)
|
||||
return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline;
|
||||
|
||||
//DoubleWidth
|
||||
if (printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.DoubleWidth;
|
||||
|
||||
if (printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.DoubleWidth | PrintStyle.FontB;
|
||||
|
||||
if (printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline)
|
||||
return PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline;
|
||||
|
||||
//FontB
|
||||
if (printStyleModel.FontB && !printStyleModel.Underline)
|
||||
return PrintStyle.FontB;
|
||||
|
||||
if(printStyleModel.FontB && printStyleModel.Underline)
|
||||
return PrintStyle.FontB | PrintStyle.Underline;
|
||||
|
||||
//Underline
|
||||
if (printStyleModel.Underline)
|
||||
return PrintStyle.Underline;
|
||||
|
||||
return PrintStyle.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
PointOfSale/EpsonPrinterLinux/appsettings.Development.json
Normal file
13
PointOfSale/EpsonPrinterLinux/appsettings.Development.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"PrintSettings": {
|
||||
"DevicePath": "printer-output.bin",
|
||||
"PreviewPath": "printer-preview.txt"
|
||||
}
|
||||
}
|
||||
26
PointOfSale/EpsonPrinterLinux/appsettings.json
Normal file
26
PointOfSale/EpsonPrinterLinux/appsettings.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"PrintSettings": {
|
||||
"DevicePath": "/dev/usb/lp0",
|
||||
"PrintWidth": 48,
|
||||
"CodePage": "ISO8859_15_LATIN9",
|
||||
"Defaults": {
|
||||
"StoreName": "Blomster Til Alt",
|
||||
"AddressLine1": "Adelgade 88",
|
||||
"AddressLine2": "5400 Bogense",
|
||||
"Cvr": "CVR: 37144436",
|
||||
"Phone": "Tlf: 41 82 71 66",
|
||||
"FooterLine1": "Tak for dit køb!",
|
||||
"FooterLine2": "Vi håber du bliver glad for dine varer.",
|
||||
"Staff": "Ukendt",
|
||||
"ReceiptPrefix": "BON"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
PointOfSale/EpsonPrinterLinux/printer-output.bin
Normal file
BIN
PointOfSale/EpsonPrinterLinux/printer-output.bin
Normal file
Binary file not shown.
12
PointOfSale/EpsonPrinterLinux/printer-preview.txt
Normal file
12
PointOfSale/EpsonPrinterLinux/printer-preview.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Blomster Til Alt
|
||||
Adelgade 91
|
||||
5400 Bogense
|
||||
Tlf: 41 82 71 66
|
||||
CVR: 37 14 44 36
|
||||
Dato.........: 22.26 15-07-2026
|
||||
Ekspedient...: Jette
|
||||
Kvittering...: 15781
|
||||
Buket..............1 á 50,50 DKK : 50,50 DKK
|
||||
Total: 50,50 DKK
|
||||
Moms udgør 25% : 12,63 DKK
|
||||
Tak for handlen
|
||||
73
PointOfSale/EpsonPrinterLinux/testdata.json
Normal file
73
PointOfSale/EpsonPrinterLinux/testdata.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"logoBase64": "string",
|
||||
"header": [
|
||||
{
|
||||
"value": "F<>rste linie med <20><><EFBFBD> <20><><EFBFBD>",
|
||||
"printStyles": {
|
||||
"fontB": false,
|
||||
"bold": true,
|
||||
"doubleHeight": false,
|
||||
"doubleWidth": false,
|
||||
"underline": false
|
||||
},
|
||||
"textAlignment": 0
|
||||
},
|
||||
{
|
||||
"value": "Anden linie",
|
||||
"printStyles": {
|
||||
"fontB": false,
|
||||
"bold": true,
|
||||
"doubleHeight": false,
|
||||
"doubleWidth": false,
|
||||
"underline": false
|
||||
},
|
||||
"textAlignment": 0
|
||||
},
|
||||
{
|
||||
"value": "Tredje linie",
|
||||
"printStyles": {
|
||||
"fontB": false,
|
||||
"bold": true,
|
||||
"doubleHeight": false,
|
||||
"doubleWidth": false,
|
||||
"underline": false
|
||||
},
|
||||
"textAlignment": 0
|
||||
},
|
||||
{
|
||||
"value": "Fjerde linie",
|
||||
"printStyles": {
|
||||
"fontB": false,
|
||||
"bold": true,
|
||||
"doubleHeight": false,
|
||||
"doubleWidth": false,
|
||||
"underline": false
|
||||
},
|
||||
"textAlignment": 0
|
||||
},
|
||||
{
|
||||
"value": "Femte linie",
|
||||
"printStyles": {
|
||||
"fontB": false,
|
||||
"bold": true,
|
||||
"doubleHeight": false,
|
||||
"doubleWidth": false,
|
||||
"underline": false
|
||||
},
|
||||
"textAlignment": 0
|
||||
}
|
||||
],
|
||||
"footer": [
|
||||
{
|
||||
"value": "string",
|
||||
"printStyles": {
|
||||
"fontB": true,
|
||||
"bold": true,
|
||||
"doubleHeight": true,
|
||||
"doubleWidth": true,
|
||||
"underline": true
|
||||
},
|
||||
"textAlignment": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user