Как распечатать текстовый файл на термопринтере с помощью PrintDocument?
Я создаю приложение, используя C# с Winforms, и теперь мне нужно распечатать квитанцию о продаже на термопринтер. Для этого я создаю текстовый файл и читаю его для печати, используя PrintDocument
но я не могу этого сделать, потому что я не знаю, как настроить размер бумаги, выровнять текстовый центр по бумаге и другие конфигурации. Когда я делаю печать, текстовый файл печатается, но все грязно, и после окончания печати бумага не останавливается.
Как я мог это сделать?
пробовать.
private PrintDocument printDocument = new PrintDocument();
private static String RECEIPT = Environment.CurrentDirectory + @"\comprovantes\comprovante.txt";
private String stringToPrint = "";
private void button1_Click(object sender, EventArgs e)
{
generateReceipt();
printReceipt();
}
private void generateReceipt()
{
FileStream fs = new FileStream(COMPROVANTE, FileMode.Create);
StreamWriter writer = new StreamWriter(fs);
writer.WriteLine("==========================================");
writer.WriteLine(" NOME DA EMPRESA AQUI ");
writer.WriteLine("==========================================");
writer.Close();
fs.Close();
}
private void printReceipt()
{
FileStream fs = new FileStream(COMPROVANTE, FileMode.Open);
StreamReader sr = new StreamReader(fs);
stringToPrint = sr.ReadToEnd();
printDocument.PrinterSettings.PrinterName = DefaultPrinter.GetDefaultPrinterName();
printDocument.PrintPage += new PrintPageEventHandler(printPage);
printDocument.Print();
sr.Close();
fs.Close();
}
private void printPage(object sender, PrintPageEventArgs e)
{
int charactersOnPage = 0;
int linesPerPage = 0;
Graphics graphics = e.Graphics;
// Sets the value of charactersOnPage to the number of characters
// of stringToPrint that will fit within the bounds of the page.
graphics.MeasureString(stringToPrint, this.Font,
e.MarginBounds.Size, StringFormat.GenericTypographic,
out charactersOnPage, out linesPerPage);
// Draws the string within the bounds of the page
graphics.DrawString(stringToPrint, this.Font, Brushes.Black,
e.MarginBounds, StringFormat.GenericTypographic);
// Remove the portion of the string that has been printed.
stringToPrint = stringToPrint.Substring(charactersOnPage);
// Check to see if more pages are to be printed.
e.HasMorePages = (stringToPrint.Length > 0);
}
Я пытаюсь создать эту модель получения.
Используя RDLC, но он очень медленно начинает печатать. Я следую этому примеру
//works but slow
private void Run() {
LocalReport report = new LocalReport();
report.ReportPath = @"..\..\reports\ReciboDeVenda.rdlc";
Export(report);
Print();
}
private void Export(LocalReport report) {
string deviceInfo =
@"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
<PageWidth>8.5in</PageWidth>
<PageHeight>11in</PageHeight>
<MarginTop>0.25in</MarginTop>
<MarginLeft>0.25in</MarginLeft>
<MarginRight>0.25in</MarginRight>
<MarginBottom>0.25in</MarginBottom>
</DeviceInfo>";
Warning[] warnings;
m_streams = new List<Stream>();
report.Render("Image", deviceInfo, CreateStream,
out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
private Stream CreateStream(string name, string fileNameExtension,
Encoding encoding, string mimeType, bool willSeek) {
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}
private void Print() {
if (m_streams == null || m_streams.Count == 0)
throw new Exception("Error: no stream to print.");
PrintDocument printDoc = new PrintDocument();
printDoc.PrinterSettings.PrinterName = DefaultPrinter.GetDefaultPrinterName();
if (!printDoc.PrinterSettings.IsValid) {
throw new Exception("Error: cannot find the default printer.");
}else {
printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
m_currentPageIndex = 0;
printDoc.Print();
}
}
private void PrintPage(object sender, PrintPageEventArgs ev) {
Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]);
// Adjust rectangular area with printer margins.
Rectangle adjustedRect = new Rectangle(
ev.PageBounds.Left - (int)ev.PageSettings.HardMarginX,
ev.PageBounds.Top - (int)ev.PageSettings.HardMarginY,
ev.PageBounds.Width,
ev.PageBounds.Height);
// Draw a white background for the report
ev.Graphics.FillRectangle(Brushes.White, adjustedRect);
// Draw the report content
ev.Graphics.DrawImage(pageImage, adjustedRect);
// Prepare for the next page. Make sure we haven't hit the end.
m_currentPageIndex++;
ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
}
3 ответа
Если вы отправите простую строку на ваш printPage(object sender, PrintEventArgs e)
метод, он будет просто печатать обычный текст одним и тем же шрифтом, который выглядит "грязным" (как вы его назвали). Если вы хотите, чтобы он печатался хорошо отформатированным разными шрифтами (полужирный, обычный), вы должны сделать все это вручную:
List<string> itemList = new List<string>()
{
"201", //fill from somewhere in your code
"202"
};
private void printPage( object sender, PrintPageEventArgs e )
{
Graphics graphics = e.Graphics;
Font regular = new Font( FontFamily.GenericSansSerif, 10.0f, FontStyle.Regular );
Font bold = new Font( FontFamily.GenericSansSerif, 10.0f, FontStyle.Bold );
//print header
graphics.DrawString( "FERREIRA MATERIALS PARA CONSTRUCAO LTDA", bold, Brushes.Black, 20, 10 );
graphics.DrawString( "EST ENGENHEIRO MARCILAC, 116, SAO PAOLO - SP", regular, Brushes.Black, 30, 30 );
graphics.DrawString( "Telefone: (11)5921-3826", regular, Brushes.Black, 110, 50 );
graphics.DrawLine( Pens.Black, 80, 70, 320, 70 );
graphics.DrawString( "CUPOM NAO FISCAL", bold, Brushes.Black, 110, 80 );
graphics.DrawLine( Pens.Black, 80, 100, 320, 100 );
//print items
graphics.DrawString( "COD | DESCRICAO | QTY | X | Vir Unit | Vir Total |", bold, Brushes.Black, 10, 120 );
graphics.DrawLine( Pens.Black, 10, 140, 430, 140 );
for( int i = 0; i < itemList.Count; i++ )
{
graphics.DrawString( itemList[i].ToString(), regular, Brushes.Black, 20, 150 + i * 20 );
}
//print footer
//...
regular.Dispose();
bold.Dispose();
// Check to see if more pages are to be printed.
e.HasMorePages = ( itemList.Count > 20 );
}
Возможные улучшения на моем примере:
- Центрирование строк заголовка может быть лучше сделано с помощью
graphics.MeasureString()
, - Список элементов должен быть лучше, чем список бизнес-класса, состоящий из простой строки
Поскольку все это - большая работа, вам следует подумать об использовании RDLC или какого-либо стороннего программного обеспечения для разработки ваших документов.
Если я хочу создать отчет самостоятельно, вместо того, чтобы пытаться создать текстовый файл, я буду использовать HTML для рендеринга. Кроме того, чтобы упростить логику рендеринга и смешивание HTML-тегов и данных, я бы использовал шаблоны текста времени выполнения T4. Затем я могу передать данные в шаблон HTML и просто представить отчет. Тогда достаточно присвоить выходную строку DocumentText
свойство WebBrowser
и назовите его Print
метод.
Скачать
Вы можете клонировать или загрузить рабочий пример из https://github.com/r-aghaei/HtmlUsingRuntimeT4.
Почему HTML?
Из-за простого и гибкого форматирования. Вы можете использовать всю мощь HTML-тегов и CSS-стилей для форматирования текста. Это действительно лучше, чем использовать DrawString
,
Почему текстовые шаблоны во время выполнения?
Потому что это делает смешивание данных и HTML действительно простым, и вы можете использовать язык C# для выполнения некоторых задач, таких как вычисление суммы, итерации по записям модели и так далее. Оно использует T4
шаблонизатор и позволяет использовать шаблон во время выполнения и передавать данные в шаблон.
пример
1- Добавить модель в проект:
using System;
using System.Collections.Generic;
namespace Sample
{
public class ReportModel
{
public string CustomerName { get; set; }
public DateTime Date { get; set; }
public List<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public string Name { get; set; }
public int Price { get; set; }
public int Count { get; set; }
}
}
2- Добавьте текстовый шаблон времени выполнения (также известный как предварительно обработанный текстовый шаблон) в проект и назовите его ReportTemplate.tt
, Откройте его и добавьте такой код:
<#@ template language="C#"#>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ parameter name="Model" type="Sample.ReportModel"#>
<html>
<head>
<title></title>
<style type="text/css">
body { font-family: Calibri;width:400px;}
table { text-align:center; }
.container {width:400px; height:100%;}
</style>
</head>
<body>
<div class="container">
<h1 style="text-align:center;">Order</h1>
<hr/>
<table style="width:100%">
<tr>
<td>Customer: <#=Model.CustomerName#></td>
<td>Order Date: <#=Model.Date#></td>
</tr>
</table>
<hr/>
<table style="width:100%">
<tr><th>Index</th><th>Name</th><th>Price</th><th>Count</th><th>Sum</th></tr>
<#
int index =1;
foreach(var item in Model.OrderItems)
{
#>
<tr>
<td><#=index#></td>
<td><#=item.Name#></td>
<td><#=item.Price#></td>
<td><#=item.Count#></td>
<td><#=item.Count * item.Price#></td>
</tr>
<#
index++;
}
var total= Model.OrderItems.Sum(x=>x.Count * x.Price);
#>
<tr><td></td><td></td><td></td><th>Total:</th><th><#=total#></th></tr>
</table>
<div>
</body>
</html>
3- положить WebBrowser
укажите в форме и напишите такой код, в котором вы хотите создать отчет:
var rpt = new ReportTemplate();
rpt.Session = new Dictionary<string, object>();
rpt.Session["Model"] = new Sample.ReportModel
{
CustomerName = "Reza",
Date = DateTime.Now,
OrderItems = new List<Sample.OrderItem>()
{
new Sample.OrderItem(){Name="Product 1", Price =100, Count=2 },
new Sample.OrderItem(){Name="Product 2", Price =200, Count=3 },
new Sample.OrderItem(){Name="Product 3", Price =50, Count=1 },
}
};
rpt.Initialize();
this.webBrowser1.DocumentText= rpt.TransformText();
4- Если вы хотите распечатать отчет, вы можете позвонить:
this.webBrowser1.Print();
Вам следует позвонить Print
после того, как документ завершен. Так что если вы хотите печатать напрямую, не показывая вывод пользователю, вы можете обрабатывать DocumentCompleted
событие и звонок Print
там.
Вот результат:
Поддерживает ли ваш термопринтер OPOS?
Если это произойдет, я рекомендую вам попробовать Microsoft Point of Service