Bài viết này sẽ hướng dẫn cách thực hiện chức năng in ra giấy (Print) bằng API trong .Net Core, tích hợp chức năng in vào ứng dụng của bạn để cung cấp trải nghiệm của người dùng tốt nhất khi sử dụng dịch vụ của các bạn và mang lại nhiều lợi ích hiệu quả như là :
- Tính linh hoạt cho hệ thống.
- Tiết kiệm thời gian.
- Chính xác và tính toàn vẹn cao.
- Tích hợp dễ dàng.
- Tăng trải nghiệm người dùng.
Không lan man nửa, bắt đầu vô việc thôi nào.
- Bước 1: Chuẩn bị Máy In và Cài đặt Driver của máy.
Tuỳ theo máy in mà có mỗi Driver khác nhau, máy mà tôi đang sử dụng là IPOS.VN:
Tuỳ theo máy in mà có mỗi Driver khác nhau, máy mà tôi đang sử dụng là IPOS.VN:
- Bước 2 : Tiếp đến là cài đặt Driver cho máy in:
Mình sẽ để link Driver, có thể down về dùng nhé:
https://drive.google.com/drive/folders/1DNbNP9CI3AM3e4rCN9BH4m7QRL5Ez4Jn?usp=sharing
+ 1. Settings => Bluetooth & devices => Chọn đến Devices Print mà đã Install.
+ 2. Chọn vào Printer properties.
+ 6. Chọn Printing preferences => Paper/Quality => chọn option No Cash Drawer.
- Bước 3: Tiếp theo sẽ viết API để Print Bill.
+ Cài đặt thư viện: Thư viện ở đây chúng ta dùng đó là System.Drawing.Common.
Hoặc có thể sử dụng lệnh command sau :
- Bước 4: Sử dụng thư viện vừa install và viết 1 Function để xử lý việc in này.
+ 3. Chọn General. Có thể thay đổi tên của Máy in, để có thể dễ nhận biết hơn.
+ 4. Chọn Preferences để setting => chọn Paper/Quality => Media : No Cash Drawer.
+ 5. Tiếp đến thì chọn Ports => Chọn vào USB (Máy in có 2 cách để kết nối với máy để in hoá đơn là Wifi và USB, do hiện tại mình đang sử dụng máy là USB nên chọn vào USB, còn kết nối Wifi thì các bạn tự tìm hiểu nhé!). Sau đó Apply và Click OK nhé.
+ 7. Sau đó để test thử máy in đã được kết nối và hoạt động hay chưa. Thì Click vào Print Test Page.
- Bước 3: Tiếp theo sẽ viết API để Print Bill.
+ Cài đặt thư viện: Thư viện ở đây chúng ta dùng đó là System.Drawing.Common.
Hoặc có thể sử dụng lệnh command sau :
PM> Install-Package System.Drawing.Common
- Bước 4: Sử dụng thư viện vừa install và viết 1 Function để xử lý việc in này.
using Application.Dtos.Responses.Order;
using Application.Interfaces.Repositories.PrintBill;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Text;
namespace Infrastructure.Repositories.PrintBill
{
public class PrintBillRepository : IPrintBillRepository
{
public void PrintPage(object sender, PrintPageEventArgs e)
{
// Lấy dữ liệu của hoá đơn
var data = PrintBillData();
// Màu chữ
SolidBrush printBrush = new SolidBrush(Color.Black);
// Tên nhà hàng
string restaurantName = "LA MAISON 1888";
Font nameFont = new Font("Arial", 9, FontStyle.Bold);
// Địa chỉ
string address = "Địa chỉ: 100 Hoàng Diệu - Hải Châu - Đà Nẵng";
Font addressFont = new Font("Arial", 9, FontStyle.Regular);
// Điện thoại
string phoneNumber = "Điện thoại: 0965.223.932";
Font phoneFont = new Font("Arial", 10, FontStyle.Regular);
// Đường gạch ngang
Font lineFont = new Font("Arial", 11, FontStyle.Regular);
// Đối tượng Pen cho đường gạch đứt
Pen dashedPen = new Pen(Color.Black);
dashedPen.DashStyle = DashStyle.Dash;
// Kích thước giấy
float paperWidth = 300;
// Vị trí vẽ, chiều ngang căn giữa
PointF startPoint = new PointF((paperWidth - e.Graphics.MeasureString(restaurantName, nameFont).Width) / 2, 10);
// Vẽ chuỗi căn giữa
e.Graphics.DrawString(restaurantName, nameFont, printBrush, startPoint);
// Khoảng cách giữa các hàng tiếp theo (Cộng thêm chiều cao của đoạn trước để vẽ đoạn tiếp theo)
startPoint.Y += e.Graphics.MeasureString(restaurantName, nameFont).Height;
// In Địa chỉ
e.Graphics.DrawString(address, addressFont, printBrush, new PointF((paperWidth - e.Graphics.MeasureString(address, addressFont).Width) / 2, startPoint.Y));
// Khoảng cách giữa các hàng tiếp theo (Cộng thêm chiều cao của đoạn trước để vẽ đoạn tiếp theo)
startPoint.Y += e.Graphics.MeasureString(address, addressFont).Height;
// In Điện thoại
e.Graphics.DrawString(phoneNumber, phoneFont, printBrush, new PointF((paperWidth - e.Graphics.MeasureString(phoneNumber, phoneFont).Width) / 2, startPoint.Y));
// Khoảng cách giữa các hàng tiếp theo (Cộng thêm chiều cao của đoạn trước để vẽ đoạn tiếp theo)
startPoint.Y += e.Graphics.MeasureString(phoneNumber, phoneFont).Height;
// Đường gạch đứt
float lineY = startPoint.Y + e.Graphics.MeasureString("__________________________________________________________", lineFont).Height / 2;
float startX = 20; // Thay đổi tùy thuộc vào vị trí bạn muốn bắt đầu vẽ đường gạch đứt
float endX = paperWidth - 20; // Thay đổi tùy thuộc vào vị trí bạn muốn kết thúc vẽ đường gạch đứt
e.Graphics.DrawLine(dashedPen, startX, lineY, endX, lineY);
// Ngày bán
string saleDate = "Ngày bán: " + data.DateSale?.ToString("dd/MM/yyyy HH:mm");
Font dateFont = new Font("Arial", 9, FontStyle.Regular);
float dateY = startPoint.Y + e.Graphics.MeasureString("__________________________________________________________", lineFont).Height;
float dateX = 20; // Điều chỉnh vị trí x cho ngày bán
e.Graphics.DrawString(saleDate, dateFont, printBrush, new PointF(dateX, dateY));
// Hoá đơn tạm tính
string receiptHeader = "HĐ TẠM TÍNH";
Font headerFont = new Font("Arial", 11, FontStyle.Bold);
float headerY = dateY + e.Graphics.MeasureString(saleDate, dateFont).Height + 5; // Cộng thêm một khoảng trống
e.Graphics.DrawString(receiptHeader, headerFont, printBrush, new PointF((paperWidth - e.Graphics.MeasureString(receiptHeader, headerFont).Width) / 2, headerY));
// Người bán (giả sử bạn có một hàm để lấy thông tin từ cơ sở dữ liệu)
string salesman = data.Seller;
Font salesmanFont = new Font("Arial", 9, FontStyle.Regular);
float salesmanY = headerY + e.Graphics.MeasureString(receiptHeader, headerFont).Height + 5; // Cộng thêm một khoảng trống
float salesmanX = 20; // Điều chỉnh vị trí cho chữ người bán
e.Graphics.DrawString("Người bán: " + salesman, salesmanFont, printBrush, new PointF(salesmanX, salesmanY));
// Giờ vào
string saleDateTime = "Giờ vào: " + data.TimeEnter?.ToString("dd/MM/yyyy HH:mm");
Font dateTimeFont = new Font("Arial", 9, FontStyle.Regular);
float dateTimeY = salesmanY + e.Graphics.MeasureString(salesman, salesmanFont).Height + 5; // Cộng thêm một khoảng trống
float dateTimeX = 20; // Điều chỉnh vị trí cho giờ vào
e.Graphics.DrawString(saleDateTime, dateTimeFont, printBrush, new PointF(dateTimeX, dateTimeY));
// Bàn
string tableOrder = data.TableOrder;
Font tableFont = new Font("Arial", 9, FontStyle.Regular);
float tableSpacing = 8; // Tính chiều cao của đoạn văn bản "Bàn 5B"
float tableTextHeight = e.Graphics.MeasureString(tableOrder, tableFont).Height; // Vị trí y cho thông tin "Bàn 5B"
float tableY = dateTimeY + e.Graphics.MeasureString(saleDateTime, dateTimeFont).Height + tableSpacing; // Điều chỉnh vị trí x cho Bàn số 5
float tableX = 20;
e.Graphics.DrawString("Bàn:" + tableOrder, tableFont, printBrush, new PointF(tableX, tableY)); // Vẽ chuỗi thông tin "Bàn 5B"
// Đường gạch đứt
float lineY2 = tableY + tableTextHeight + tableSpacing; // Chuyển đoạn nét đứt xuống dưới thông tin "Bàn 5B"
float startX2 = 20; // Thay đổi tùy thuộc vào vị trí bạn muốn bắt đầu vẽ đường gạch đứt
float endX2 = paperWidth - 20; // Thay đổi tùy thuộc vào vị trí bạn muốn kết thúc vẽ đường gạch đứt
e.Graphics.DrawLine(dashedPen, startX2, lineY2, endX2, lineY2);
// Đơn giá
string columnName1 = "Đơn giá";
Font columnFont1 = new Font("Arial", 9, FontStyle.Bold);
float columnWidth1 = e.Graphics.MeasureString(columnName1, columnFont1).Width;
float columnX1 = 20;
// SL (Số lượng)
string columnName2 = "SL";
Font columnFont2 = new Font("Arial", 9, FontStyle.Bold);
float columnWidth2 = e.Graphics.MeasureString(columnName2, columnFont2).Width;
float columnX2 = columnX1 + columnWidth1 + 60; // 20 là khoảng cách giữa các cột
// Thành tiền
string columnName3 = "Thành tiền";
Font columnFont3 = new Font("Arial", 9, FontStyle.Bold);
float columnWidth3 = e.Graphics.MeasureString(columnName3, columnFont3).Width;
float columnX3 = columnX2 + columnWidth2 + 55; // 20 là khoảng cách giữa các cột
// Vẽ cột "Đơn giá"
e.Graphics.DrawString(columnName1, columnFont1, printBrush, new PointF(columnX1, lineY2 + 10));// 10 là khoảng cách giữa đường gạch và cột
// Vẽ cột "SL"
e.Graphics.DrawString(columnName2, columnFont2, printBrush, new PointF(columnX2, lineY2 + 10));// 10 là khoảng cách giữa đường gạch và cột
// Vẽ cột "Thành tiền"
e.Graphics.DrawString(columnName3, columnFont3, printBrush, new PointF(columnX3, lineY2 + 10));// 10 là khoảng cách giữa đường gạch và cột
// Vẽ đường gạch dưới cột
float lineY3 = lineY2 + 24 + e.Graphics.MeasureString(columnName1, columnFont1).Height;
e.Graphics.DrawLine(dashedPen, columnX1, lineY3, columnX3 + columnWidth3, lineY3);
float orderY = lineY3 + 20; // Set lại khoảng cách giữa các hàng tiếp theo.
// Dữ liệu sản phẩm của hoá đơn đó.
foreach (var item in data.OrderItems)
{
// Thông tin đơn hàng
string productName = item.Name; // Tên sản phầm
int? quantity = item.Quantity; // Số lượng
decimal? unitPrice = item.Price; // Giá
// Đo chiều dài của productName.
float productNameWidth = e.Graphics.MeasureString(productName, new Font("Arial", 9, FontStyle.Regular)).Width;
// Vị trí y cho thông tin sản phẩm và các cột
float productInfoY = orderY;
// Nếu chiều dài của productName vượt quá khối lượng giấy, tự động xuống dòng
if (productNameWidth > paperWidth - columnWidth1 - columnWidth2 - columnWidth3)
{
string[] productNameLines = WordWrap(productName, paperWidth, new Font("Arial", 9, FontStyle.Regular), e);
// Vẽ từng dòng của productName
foreach (string line in productNameLines)
{
e.Graphics.DrawString(line, new Font("Arial", 9, FontStyle.Regular), printBrush, new PointF(columnX1, productInfoY));
// Tăng vị trí y cho các cột
productInfoY += e.Graphics.MeasureString(line, new Font("Arial", 9, FontStyle.Regular)).Height;
}
}
else
{
// Vẽ tên sản phẩm trên dòng hiện tại
e.Graphics.DrawString(productName, new Font("Arial", 9, FontStyle.Regular), printBrush, new PointF(columnX1, productInfoY));
// Tăng vị trí y cho các cột
productInfoY += e.Graphics.MeasureString(productName, new Font("Arial", 9, FontStyle.Regular)).Height;
}
// Vẽ giá trị của từng cột với font thông thường
e.Graphics.DrawString(unitPrice?.ToString("N0"), new Font("Arial", 9, FontStyle.Regular), printBrush, new PointF(columnX1, productInfoY));
e.Graphics.DrawString(quantity.ToString(), new Font("Arial", 9, FontStyle.Regular), printBrush, new PointF(columnX2, productInfoY));
e.Graphics.DrawString((unitPrice * quantity)?.ToString("N0"), new Font("Arial", 9, FontStyle.Regular), printBrush, new PointF(columnX3, productInfoY));
// Vẽ đường gạch dưới
float lineItem = productInfoY + 10 + e.Graphics.MeasureString(unitPrice?.ToString("N0"), new Font("Arial", 9, FontStyle.Regular)).Height; // 24 là khoảng cách giữa dòng và đường gạch
e.Graphics.DrawLine(dashedPen, columnX1, lineItem, columnX3 + columnWidth3, lineItem);
orderY = lineItem + 10; // 10 là khoảng cách giữa dòng và dòng tiếp theo
}
// Tổng tiền hàng
decimal? totalAmount = data.Total;
string totalAmountText = "Tổng tiền hàng:";
e.Graphics.DrawString(totalAmountText, columnFont1, printBrush, new PointF(columnX1 + 50, orderY));
e.Graphics.DrawString(totalAmount?.ToString("N0"), columnFont3, printBrush, new PointF(columnX3, orderY));
// Cập nhật giá trị orderY cho các dòng tiếp theo
float totalAmountHeight = e.Graphics.MeasureString(totalAmountText, columnFont1).Height;
orderY += totalAmountHeight + 5; // 20 là khoảng cách giữa dòng và dòng tiếp theo
// Chiết khấu
decimal? discount = data.Discount;
string discountText = "Chiết khấu:";
e.Graphics.DrawString(discountText, columnFont1, printBrush, new PointF(columnX1 + 50, orderY));
e.Graphics.DrawString(discount?.ToString("N0"), columnFont3, printBrush, new PointF(columnX3, orderY));
// Cập nhật giá trị orderY cho các dòng tiếp theo
float discountHeight = e.Graphics.MeasureString(discountText, columnFont1).Height;
orderY += discountHeight + 5; // 20 là khoảng cách giữa dòng và dòng tiếp theo
// Tổng cộng
decimal? grandTotal = totalAmount - discount;
string grandTotalText = "Tổng cộng:";
e.Graphics.DrawString(grandTotalText, columnFont1, printBrush, new PointF(columnX1 + 50, orderY));
e.Graphics.DrawString(grandTotal?.ToString("N0"), columnFont3, printBrush, new PointF(columnX3, orderY));
}
//Chia một đoạn văn bản thành các dòng sao cho chiều rộng của mỗi dòng không vượt quá một giới hạn (maxWidth
).
private static string[] WordWrap(string text, float maxWidth, Font font, PrintPageEventArgs e)
{
List lines = new List();
StringBuilder currentLine = new StringBuilder();
string[] words = text.Split(' ');
foreach (string word in words)
{
if (currentLine.Length == 0)
{
currentLine.Append(word);
}
else
{
string testString = currentLine.ToString() + " " + word;
if (e.Graphics.MeasureString(testString, font).Width < maxWidth)
{
currentLine.Append(" " + word);
}
else
{
lines.Add(currentLine.ToString());
currentLine.Clear();
currentLine.Append(word);
}
}
}
lines.Add(currentLine.ToString());
return lines.ToArray();
}
}
}
Dưới các đoạn code thì đã có comment đoạn code đó chức năng và mục đích nó làm gì.
Sau đó tạo function với dữ liệu mẫu để test chức năng in như thế nào:
public PrintBillResponse PrintBillData()
{
var printBillResponse = new PrintBillResponse
{
Id = 1,
DateSale = DateTime.Now,
Seller = "Nguyễn Phước Lê Hiếu",
TimeEnter = DateTime.Now.AddHours(-1),
TableOrder = "Bàn 5",
Total = 26660000,
Discount = 0,
TotalAmount = 26660000,
OrderItems = new List
{
new OrderItem { Id = 1, Name = "Cua hoàng đế xanh (Blue King Crab)", Price = 2300000, Quantity = 2 },
new OrderItem { Id = 1, Name = "Tôm hùm Alaska", Price = 1300000, Quantity = 2 },
new OrderItem { Id = 1, Name = "Trứng cá tầm", Price = 4700000, Quantity = 1 },
new OrderItem { Id = 1, Name = "Gà chín cựa", Price = 12000000, Quantity = 1 },
new OrderItem { Id = 1, Name = "Bia ST.Sebastiaan Dark Ale", Price = 230000, Quantity = 12 },
}
};
return printBillResponse;
}
- Ở Controller tạo API => và gọi hàm PrintPage() ở Repository là xong.
using Application.Interfaces.Repositories.PrintBill;
using Microsoft.AspNetCore.Mvc;
using System.Drawing.Printing;
namespace WebApi.Controllers.V1
{
[Route("api/v{version:apiVersion}/print-bill")]
[ApiController]
public class PrintController : Controller
{
private readonly IPrintBillRepository _printBillRepository;
public PrintController(IPrintBillRepository printBillRepository)
{
_printBillRepository = printBillRepository;
}
[HttpGet]
public void PrintBill()
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler(_printBillRepository.PrintPage);
// Chọn máy in mặc định
pd.PrinterSettings.PrinterName = PrinterSettings.InstalledPrinters[0];
// Kích thước giấy và hướng in
pd.DefaultPageSettings.PaperSize = new PaperSize("Custom", 300, 0);
pd.DefaultPageSettings.Landscape = false;
// In
pd.Print();
}
}
}
Vậy là xong hoàn thành phần code, giờ test thôi:
Và kết quả sau khi submit API:
Vậy là đã oke rồi nhé. Bài viết còn nhiều thiếu sót mong mọi người góp ý, để mình cải thiện hơn. Cảm ơn !
Chú ý: Thư viện này hiện tại không được hổ trợ trên Linux mọi người nhé.
in cái bill lên màu được không bạn êi
Trả lờiXóaHaha, để mua máy khác nhoa
Trả lờiXóa