Projektdateien hinzufügen.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<Solution>
|
||||
<Project Path="BonNotiz/BonNotiz.csproj" />
|
||||
</Solution>
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
<TargetFramework>net10.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<StartupObject>BonNotiz.Program</StartupObject>
|
||||
<ErrorReport>none</ErrorReport>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,322 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Drawing;
|
||||
|
||||
namespace KohaCompanion.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Stellt einen zustandsbehafteten ESC/POS-Drucker dar.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Diese Klasse bietet Methoden, die die ESC/POS-Befehle kapseln.
|
||||
/// Zum Schreiben eines Strings können Sie die Write(String)-Methode verwenden.
|
||||
///
|
||||
/// Bei jedem Aufruf einer Methode werden dabei die Kommandos in einen internen
|
||||
/// Puffer geschrieben, der am Schluss mit GetCurrentPuffer() geholt werden
|
||||
/// kann, um das Bytearray an den Drucker zu senden.
|
||||
///
|
||||
/// Quelle: https://www.vbarchiv.net/tipps/tipp_2375-kassenbon-drucker-mit-vbnet-oder-c-per-esc-pos-befehle-ansprechen.html
|
||||
/// Benötigt nuget-Paket: System.Text.Encoding.CodePages
|
||||
/// Originalautor: Konstantin Preißer
|
||||
/// Originallizenz: MIT
|
||||
/// Angepaßt an C# von Anna Christina Naß
|
||||
/// </remarks>
|
||||
|
||||
public class EscPosPrinter
|
||||
{
|
||||
public enum Alignment
|
||||
{
|
||||
Left = 0,
|
||||
Center,
|
||||
Right
|
||||
}
|
||||
|
||||
public class PrinterCodepage
|
||||
{
|
||||
public static readonly PrinterCodepage Cp437 = new PrinterCodepage(0, CodePagesEncodingProvider.Instance.GetEncoding(437));
|
||||
public static readonly PrinterCodepage Cp852 = new PrinterCodepage(18, CodePagesEncodingProvider.Instance.GetEncoding(852));
|
||||
public static readonly PrinterCodepage Cp866 = new PrinterCodepage(17, CodePagesEncodingProvider.Instance.GetEncoding(866));
|
||||
public static readonly PrinterCodepage Cp1252 = new PrinterCodepage(16, CodePagesEncodingProvider.Instance.GetEncoding(1252));
|
||||
private readonly byte _pageNumber;
|
||||
private readonly Encoding? _encoding;
|
||||
|
||||
private PrinterCodepage(byte pageNumber, Encoding? encoding)
|
||||
{
|
||||
this._pageNumber = pageNumber;
|
||||
this._encoding = encoding;
|
||||
}
|
||||
|
||||
public byte PageNumber { get { return _pageNumber; } }
|
||||
|
||||
public Encoding Encoding { get { return _encoding; } }
|
||||
}
|
||||
|
||||
// Steuercodes für neue Zeile
|
||||
private static readonly byte[] newLineBytes = { 0xA, 0xD };
|
||||
|
||||
// Interner Buffer
|
||||
private MemoryStream memstr = new();
|
||||
|
||||
// Zustand des Druckers
|
||||
private PrinterCodepage printerEnc = PrinterCodepage.Cp437;
|
||||
private bool underline = false;
|
||||
private bool underlineDouble = false;
|
||||
private bool emphasized = false;
|
||||
//private bool redColor = false; * unused
|
||||
private int currentFontX = 0;
|
||||
private int currentFontY = 0;
|
||||
//private bool panelButtonsEnabled = true; * unused
|
||||
private Alignment _alignment = Alignment.Left;
|
||||
|
||||
/// <summary>
|
||||
/// Erstellt eine neue Instanz. Dadurch wird automatisch ein
|
||||
/// "Initialize Printer"-Command (ESC '@') in den Puffer geschrieben, der
|
||||
/// den Drucker auf die Standardwerte zurücksetzt, damit der Zustand des
|
||||
/// Druckers dem Zustand dieses Objektes entspricht.
|
||||
/// </summary>
|
||||
public EscPosPrinter()
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
||||
// Initialize Printer Command senden
|
||||
byte[] buf = new byte[] { 0x1B, 0x40 };
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schreibt den angegebenen String.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Bei diesem Vorgang werden automatisch
|
||||
/// Bytes, die in den ASCII-Steuerzeichenbereich fallen (0x00-0x1F sowie 0x7F),
|
||||
/// durch Leerzeichen ersetzt. So wird verhindert, dass Strings aus fremden
|
||||
/// Quellen den Zustand des Druckers verändern können.
|
||||
///
|
||||
/// Dies betrifft auch Zeilenumbrüche. Um einen Zeilenumbruch zu schreiben,
|
||||
/// verwenden Sie WriteLine() oder WriteLine(String).
|
||||
///
|
||||
/// Beachten Sie, dass bei den meisten Bondruckern eine Zeile erst gedruckt
|
||||
/// wird, sobald diese mit einem Zeilenumbruch abgeschlossen wird (auch wenn
|
||||
/// die Zeile voll ist, also so viele Zeichen belegt, wie der Drucker in eine
|
||||
/// Zeile drucken kann).
|
||||
///
|
||||
/// Beachten Sie, dass ESC/POS-Drucker standardmäßig die DOS-Codepage 437
|
||||
/// verwenden, die u. a.Rahmenzeichen, einige griechische Zeichen und
|
||||
/// mathematischen Symbole enthält. In den meisten Fällen empfiehlt sich für
|
||||
/// Textdruck (v.a. europäische Texte) aber ein Umschalten auf die Windows-
|
||||
/// Codepage 1252 (Methode SetCodepage(PrinterCodepage)), da hier mehr
|
||||
/// unterschiedliche Buchstaben (z.B. "ß"), typographische Satzzeichen und
|
||||
/// auch das €-Zeichen enthalten sind.
|
||||
///
|
||||
/// Für Texte in anderen europäischen Sprachen empfehlen sich noch die
|
||||
/// Codepage 852, die Zeichen für mitteleuropäische Sprachen enthält, sowie
|
||||
/// die Codepage 866 mit kyrillischen Zeichen.
|
||||
/// </remarks>
|
||||
/// <param name="str">der String, der ausgegeben werden soll</param>
|
||||
public void Write(string str)
|
||||
{
|
||||
byte[] buf = printerEnc.Encoding.GetBytes(str);
|
||||
|
||||
// Aufpassen, dass kein ASCII-Steuerzeichen vorkommt.
|
||||
// For i As Integer = 0 To buf.Length - 1
|
||||
// Dim c As Byte = buf(i)
|
||||
// If Not (c >= &H20 AndAlso c <> &H7F) Then
|
||||
// buf(i) = CByte(AscW(" "c))
|
||||
// End If
|
||||
// Next
|
||||
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schreibt einen Zeilenumbruch (LF) zum Abschließen der aktuellen Zeile.
|
||||
/// </summary>
|
||||
public void WriteLine()
|
||||
{
|
||||
memstr.Write(newLineBytes, 0, newLineBytes.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schreibt den angegebenen String und danach einen Zeilenumbruch (LF).
|
||||
/// Siehe Dokumentation zur Write(String)-Methode.
|
||||
/// </summary>
|
||||
/// <param name="str">der String, der ausgegeben werden soll</param>
|
||||
public void WriteLine(string str)
|
||||
{
|
||||
Write(str);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ESC '*' 33 nL nH d1…dk:
|
||||
/// Druckt das angegebene Bild in Schwarz/Weiß. Die Höhe muss hierbei ein
|
||||
/// Vielfaches von 24 sein, da das Bild wie Textzeilen ohne Zeilenabstand
|
||||
/// gedruckt wird und eine Zeile (Font A) aus 24 Pixeln besteht. Die Breite
|
||||
/// sollte der Druckerauflösung entsprechen (z.B. 384 Pixel beim PRP-058-
|
||||
/// Drucker mit 32 Zeichen pro Zeile).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Die Druckauflösung hängt vom Druckermodell ab und beträgt normalerweise
|
||||
/// 203,2 dpi (z.B. PRP-058) oder 180 dpi.
|
||||
/// </remarks>
|
||||
/// <param name="bmp">Die zu druckende Bitmap</param>
|
||||
public void PrintImage(Bitmap bmp)
|
||||
{
|
||||
if (bmp.Height % 24 != 0)
|
||||
throw new ArgumentException("Die Bildhöhe muss ein Vielfaches von 24 sein.");
|
||||
if (bmp.Width > 0x3FF)
|
||||
throw new ArgumentException("Die Bildbreite darf nicht größer als 1023 sein.");
|
||||
|
||||
byte[] buf;
|
||||
|
||||
byte[] zeilenAnfang = new byte[] { 0x1B, 0x2A, 33, System.Convert.ToByte(bmp.Width & 0xFF), System.Convert.ToByte((bmp.Width >> 8) & 0xFF) };
|
||||
byte[] bildBuf = new byte[bmp.Width * 3 - 1 + 1];
|
||||
|
||||
for (int i = 0; i <= bmp.Height / 24 - 1; i++)
|
||||
{
|
||||
// Durch die einzelnen Zeilen gehen
|
||||
buf = zeilenAnfang;
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
|
||||
buf = bildBuf;
|
||||
Array.Clear(buf, 0, buf.Length);
|
||||
|
||||
for (int x = 0; x <= bmp.Width - 1; x++)
|
||||
{
|
||||
for (int y = 0; y <= 23; y++)
|
||||
{
|
||||
int byteIdx = y / 8 + x * 3;
|
||||
Color c = bmp.GetPixel(x, i * 24 + y);
|
||||
bool bit = c.GetBrightness() < 0.5F;
|
||||
if (bit)
|
||||
buf[byteIdx] = (byte)(buf[byteIdx] | System.Convert.ToByte(1 << (7 - (y % 8))));
|
||||
}
|
||||
}
|
||||
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
|
||||
if (i != bmp.Height / 24 - 1)
|
||||
// ESC J n für Paper Feed (n (hier 0) müsste eigentlich 48 sein für eine
|
||||
// Zeile, aber geht mit kleineren Werten auch)
|
||||
// Nicht LF verwenden, weil bei LF der normale Zeilenabstand verwendet
|
||||
// wird!
|
||||
buf = new byte[] { 0x1B, 0x4A, 0 };
|
||||
else
|
||||
// Am Schluss normaler Zeilenabstand nach unten.
|
||||
buf = new byte[] { 0xA };
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ESC '-' n:
|
||||
/// Aktiviert oder deaktiviert den Underline-Modus.
|
||||
/// </summary>
|
||||
/// <param name="value">true, wenn der Underline-Modus aktiviert werden soll,
|
||||
/// sonst false</param>
|
||||
/// <param name="doubleThickness">true, wenn die Linie 2 Pixel statt 1 Pixel
|
||||
/// dick sein soll</param>
|
||||
public void SetUnderline(bool value, bool doubleThickness)
|
||||
{
|
||||
if (value != underline || (value & doubleThickness != underlineDouble))
|
||||
{
|
||||
underline = value;
|
||||
underlineDouble = doubleThickness;
|
||||
byte[] buf = new byte[] { 0x1B, 0x2D, System.Convert.ToByte(value ? doubleThickness ? 2 : 1 : 0) };
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ESC 'E' n:
|
||||
/// Aktiviert oder deaktiviert den Fettschrift-Modus.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public void SetEmphasized(bool value)
|
||||
{
|
||||
if (value != emphasized)
|
||||
{
|
||||
emphasized = value;
|
||||
byte[] buf = new byte[] { 0x1B, 0x45, System.Convert.ToByte(value ? 1 : 0) };
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GS '!' n:
|
||||
/// Ändert die Font-Größe (Parameter jeweils von 0-7).
|
||||
/// Standardwerte: x = 0, y = 0
|
||||
/// </summary>
|
||||
/// <param name="x">Horizontale Größe</param>
|
||||
/// <param name="y">Vertikale Größe</param>
|
||||
public void SetFontSize(int x, int y)
|
||||
{
|
||||
if ((x < 0 || x > 7) || (y < 0 || y > 7))
|
||||
throw new ArgumentException("x und y müssen im Bereich 0-7 liegen.");
|
||||
|
||||
if (x != currentFontX || y != currentFontY)
|
||||
{
|
||||
currentFontX = x;
|
||||
currentFontY = y;
|
||||
|
||||
byte[] buf = new byte[] { 0x1D, 0x21, System.Convert.ToByte((x << 4) | y) };
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ESC 't' n:
|
||||
/// Schaltet auf die angegebene Codepage um.
|
||||
/// </summary>
|
||||
public void SetCodepage(PrinterCodepage codepage)
|
||||
{
|
||||
if (printerEnc != codepage)
|
||||
{
|
||||
printerEnc = codepage;
|
||||
byte[] buf = new byte[] { 0x1B, 0x74, codepage.PageNumber };
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ESC 'a' n:
|
||||
/// Setzt die angegebene Textausrichtung.
|
||||
/// </summary>
|
||||
/// <param name="alignment"></param>
|
||||
public void SetAlignment(Alignment alignment)
|
||||
{
|
||||
if (_alignment != alignment)
|
||||
{
|
||||
_alignment = alignment;
|
||||
byte[] buf = new byte[] { 0x1B, 0x61, System.Convert.ToByte(alignment) };
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GS 'V' m:
|
||||
/// Schneidet das Papier (nur bei Modellen mit einer Auto-Cut-Funktion).
|
||||
/// </summary>
|
||||
/// <param name="fullCut">true, wenn ein voller Schnitt durchgeführt
|
||||
/// werden soll; false, wenn ein kleines Stück freigelassen
|
||||
/// werden soll</param>
|
||||
public void CutPaper(bool fullCut)
|
||||
{
|
||||
byte[] buf = new byte[] { 0x1D, 0x56, System.Convert.ToByte(fullCut ? 65 : 66), 0x40 };
|
||||
memstr.Write(buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gibt den aktuellen Pufferinhalt zurück und leert diesen anschließend.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public byte[] GetCurrentBuffer()
|
||||
{
|
||||
byte[] buf = memstr.ToArray();
|
||||
memstr.Close();
|
||||
memstr = new MemoryStream();
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+89
@@ -0,0 +1,89 @@
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BonNotiz
|
||||
{
|
||||
partial class Main
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
TextBox = new TextBox();
|
||||
PrinterSelect = new ComboBox();
|
||||
Print = new Button();
|
||||
SuspendLayout();
|
||||
//
|
||||
// TextBox
|
||||
//
|
||||
TextBox.AcceptsReturn = true;
|
||||
TextBox.Font = new Font("Consolas", 12F, FontStyle.Regular, GraphicsUnit.Point, 0);
|
||||
TextBox.Location = new Point(12, 12);
|
||||
TextBox.Multiline = true;
|
||||
TextBox.Name = "TextBox";
|
||||
TextBox.ScrollBars = ScrollBars.Vertical;
|
||||
TextBox.Size = new Size(470, 339);
|
||||
TextBox.TabIndex = 0;
|
||||
TextBox.Text = "Text eingeben...";
|
||||
//
|
||||
// PrinterSelect
|
||||
//
|
||||
PrinterSelect.FormattingEnabled = true;
|
||||
PrinterSelect.Location = new Point(12, 357);
|
||||
PrinterSelect.Name = "PrinterSelect";
|
||||
PrinterSelect.Size = new Size(177, 23);
|
||||
PrinterSelect.TabIndex = 2;
|
||||
//
|
||||
// Print
|
||||
//
|
||||
Print.Location = new Point(407, 357);
|
||||
Print.Name = "Print";
|
||||
Print.Size = new Size(75, 23);
|
||||
Print.TabIndex = 3;
|
||||
Print.Text = "&Drucken";
|
||||
Print.UseVisualStyleBackColor = true;
|
||||
Print.Click += Print_Click;
|
||||
//
|
||||
// Main
|
||||
//
|
||||
AutoScaleDimensions = new SizeF(7F, 15F);
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(493, 391);
|
||||
Controls.Add(Print);
|
||||
Controls.Add(PrinterSelect);
|
||||
Controls.Add(TextBox);
|
||||
Name = "Main";
|
||||
Text = "BonNotiz";
|
||||
ResumeLayout(false);
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private TextBox TextBox;
|
||||
private ComboBox PrinterSelect;
|
||||
private Button Print;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using KohaCompanion.Shared;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BonNotiz
|
||||
{
|
||||
public partial class Main : Form
|
||||
{
|
||||
public Main()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
FillPrinters();
|
||||
}
|
||||
|
||||
private void FillPrinters()
|
||||
{
|
||||
// Auswahllisten für Drucker befüllen
|
||||
foreach (String p in System.Drawing.Printing.PrinterSettings.InstalledPrinters)
|
||||
{
|
||||
PrinterSelect.Items.Add(p);
|
||||
}
|
||||
|
||||
foreach (String p in PrinterSelect.Items)
|
||||
{
|
||||
if (p.StartsWith("Epson"))
|
||||
{
|
||||
PrinterSelect.SelectedItem = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Drucken(String bondrucker, String text)
|
||||
{
|
||||
EscPosPrinter pos = new();
|
||||
pos.Write(text);
|
||||
pos.WriteLine();
|
||||
pos.WriteLine();
|
||||
|
||||
pos.CutPaper(true);
|
||||
Byte[] buf = pos.GetCurrentBuffer();
|
||||
|
||||
using RawPrinter prn = new(bondrucker);
|
||||
using RawPrinter.RawDocumentStream doc = prn.CreateDocument("BonNotiz");
|
||||
doc.Write(buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
private void Print_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (PrinterSelect.SelectedItem == null || PrinterSelect.SelectedItem.ToString() == null)
|
||||
{
|
||||
MessageBox.Show("Kein Drucker ausgewählt.", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
Drucken(PrinterSelect.SelectedItem.ToString()!, TextBox.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BonNotiz
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
// To customize application configuration such as set high DPI settings or default font,
|
||||
// see https://aka.ms/applicationconfiguration.
|
||||
ApplicationConfiguration.Initialize();
|
||||
Application.Run(new Main());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace KohaCompanion.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Eine Klasse zum Kapseln eines Druckers. Mit der Methode
|
||||
/// <see cref="RawPrinter.CreateDocument">CreateDocument(String)</see>
|
||||
/// kann ein neues Dokument im RAW-Format erstellt werden.
|
||||
///
|
||||
/// Pro RawPrinter-Instanz kann immer nur eine RawDocumentStream-Instanz
|
||||
/// gleichzeitig aktiv sein. Nach der Freigabe einer RawDocumentStream-
|
||||
/// Instanz kann wieder ein neues Dokument erstellt werden.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Diese Klasse ist nicht threadsicher. Wenn verschiedene Threads drucken
|
||||
/// wollen, muss für jeden Thread ein eigenens RawPrinter-Objekt
|
||||
/// erstellt werden.
|
||||
///
|
||||
/// Quelle: https://www.vbarchiv.net/tipps/tipp_2375-kassenbon-drucker-mit-vbnet-oder-c-per-esc-pos-befehle-ansprechen.html
|
||||
/// Originalautor: Konstantin Preißer
|
||||
/// Originallizenz: MIT
|
||||
/// Angepaßt an C# von Anna Christina Naß
|
||||
/// </remarks>
|
||||
|
||||
public class RawPrinter : IDisposable
|
||||
{
|
||||
// API-Deklarationen
|
||||
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||
private static extern bool OpenPrinter(string szPrinter, ref IntPtr hPrinter, IntPtr pd);
|
||||
|
||||
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||
private static extern bool ClosePrinter(IntPtr hPrinter);
|
||||
|
||||
[DllImport("winspool.drv", EntryPoint = "GetPrinterDriver2W", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||
private static extern bool GetPrinterDriver2(IntPtr hWnd, IntPtr hPrinter, string? pEnvironment, int Level, IntPtr pDriverInfo, int cbBuf, ref int pcbNeeded);
|
||||
|
||||
private const int ERROR_INSUFFICIENT_BUFFER = 122;
|
||||
private const int PRINTER_DRIVER_XPS = 0x2;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private class DRIVER_INFO_8
|
||||
{
|
||||
public uint cVersion;
|
||||
public string? pName;
|
||||
public string? pEnvironment;
|
||||
public string? pDriverPath;
|
||||
public string? pDataFile;
|
||||
public string? pConfigFile;
|
||||
public string? pHelpFile;
|
||||
public string? pDependentFiles;
|
||||
public string? pMonitorName;
|
||||
public string? pDefaultDataType;
|
||||
public string? pszzPreviousNames;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME ftDriverDate;
|
||||
public ulong dwlDriverVersion;
|
||||
public string? pszMfgName;
|
||||
public string? pszOEMUrl;
|
||||
public string? pszHardwareID;
|
||||
public string? pszProvider;
|
||||
public string? pszPrintProcessor;
|
||||
public string? pszVendorSetup;
|
||||
public string? pszzColorProfiles;
|
||||
public string? pszInfPath;
|
||||
public uint dwPrinterDriverAttributes;
|
||||
public string? pszzCoreDriverDependencies;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME ftMinInboxDriverVerDate;
|
||||
public ulong dwlMinInboxDriverVerVersion;
|
||||
}
|
||||
|
||||
// Instanz-Variablen
|
||||
private readonly IntPtr hPrinter = IntPtr.Zero;
|
||||
private readonly bool xpsDriver;
|
||||
private RawDocumentStream? currentDoc = null;
|
||||
private bool disposed = false;
|
||||
|
||||
private IntPtr HandlePrinter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("RawPrinter");
|
||||
|
||||
return hPrinter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erstellt eine neue Instanz.
|
||||
/// </summary>
|
||||
/// <param name="printerName">Der Name des zu verwendenden Druckers</param>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">Beim Aufruf einer
|
||||
/// Win32-API trat ein Fehler auf.</exception>
|
||||
public RawPrinter(string printerName)
|
||||
{
|
||||
CheckApiCall(OpenPrinter(printerName, ref hPrinter, IntPtr.Zero));
|
||||
|
||||
xpsDriver = IsXpsDriver();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Überprüft den Rückgabewert einer Win32-API-Funktion und wirft bei Bedarf
|
||||
/// eine Exception.
|
||||
/// </summary>
|
||||
/// <param name="retVal"></param>
|
||||
private static void CheckApiCall(bool retVal)
|
||||
{
|
||||
if (!retVal)
|
||||
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gibt die nativen Resourcen dieses Objekts frei.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">Beim Aufruf einer
|
||||
/// Win32-API trat ein Fehler auf.</exception>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
ClosePrinter(HandlePrinter);
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~RawPrinter()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private bool canCreateDocument = false;
|
||||
/// <summary>
|
||||
/// Erstellt ein neues RawPrintDocument.
|
||||
/// </summary>
|
||||
/// <param name="docName">Der Name des Dokuments</param>
|
||||
/// <returns>Das neue Dokument</returns>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">Beim Aufruf einer
|
||||
/// Win32-API trat ein Fehler auf.</exception>
|
||||
/// <exception cref="InvalidOperationException">Es wurde bereits ein Dokument
|
||||
/// für diesen Drucker erstellt und noch nicht freigegeben.</exception>
|
||||
public RawDocumentStream CreateDocument(string docName)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("RawPrinter");
|
||||
|
||||
if (currentDoc != null)
|
||||
throw new InvalidOperationException("Pro RawPrinter kann immer nur " + "1 RawDocumentStream-Objekt erzeugt werden.");
|
||||
|
||||
canCreateDocument = true;
|
||||
try
|
||||
{
|
||||
currentDoc = new RawDocumentStream(this, docName);
|
||||
return currentDoc;
|
||||
}
|
||||
finally
|
||||
{
|
||||
canCreateDocument = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Überprüft, ob es sich bei dem Treiber dieses Druckers um einen
|
||||
/// XPS-Treiber handelt.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Bei Windows-Versionen kleiner als Windows 8 (NT 6.2), also
|
||||
/// Windows Vista und Windows 7, wird immer
|
||||
/// false zurückgegeben.
|
||||
/// </remarks>
|
||||
/// <returns>true, wenn es sich um einen XPS-Treiber handelt</returns>
|
||||
private bool IsXpsDriver()
|
||||
{
|
||||
int level = 8;
|
||||
|
||||
// Nachschauen, wieviel Bytes als Buffer reserviert werden müssen.
|
||||
// In diesem Fall muss GetPrinterDriver2 mit ERROR_INSUFFICIENT_BUFFER
|
||||
// failen.
|
||||
int bytesNeeded = 0;
|
||||
GetPrinterDriver2(IntPtr.Zero, HandlePrinter, null, level, IntPtr.Zero, 0, ref bytesNeeded);
|
||||
if (Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER)
|
||||
CheckApiCall(false);
|
||||
|
||||
DRIVER_INFO_8 driverInf;
|
||||
|
||||
// Nativen Speicher mit geforderter Größe erstellen
|
||||
int pBytesLength = Math.Max(bytesNeeded, Marshal.SizeOf(typeof(DRIVER_INFO_8)));
|
||||
IntPtr pBytes = Marshal.AllocHGlobal(pBytesLength);
|
||||
try
|
||||
{
|
||||
CheckApiCall(GetPrinterDriver2(IntPtr.Zero, HandlePrinter, null, level, pBytes, pBytesLength, ref bytesNeeded));
|
||||
|
||||
// Der Anfang des Bytesarrays enthält die Structure; der hintere Teil kann
|
||||
// die Strings enthalten, auf die die Pointer in der Structure zeigen.
|
||||
// Hier jetzt die Structure in ein verwaltetes Objekt marshallen.
|
||||
driverInf = (DRIVER_INFO_8)Marshal.PtrToStructure(pBytes, typeof(DRIVER_INFO_8));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(pBytes);
|
||||
pBytes = IntPtr.Zero;
|
||||
}// Pointer clearen
|
||||
|
||||
// Prüfen, ob das Flag PRINTER_DRIVER_XPS gesetzt ist
|
||||
return (driverInf.dwPrinterDriverAttributes & PRINTER_DRIVER_XPS) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ermöglicht das Senden von RAW-Dokumenten (Bytes) an den Drucker.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Die Methoden StartPage() und EndPage() dienen zum Unterteilen des
|
||||
/// Druckauftrags in Seiten (wichtig, wenn der Windows Spooler verwendet
|
||||
/// wird und man Dokumente senden will, die lange zum Erstellen brauchen,
|
||||
/// da dieser bis zur Fertigestellung einer Seite wartet, bevor diese
|
||||
/// tatsächlich zum Drucker gesendet wird).
|
||||
/// </remarks>
|
||||
public class RawDocumentStream : Stream
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private class DOCINFO
|
||||
{
|
||||
public string? pDocName;
|
||||
public string? pOutputFile;
|
||||
public string? pDataType;
|
||||
}
|
||||
|
||||
// API-Deklarationen
|
||||
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||
private static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In()][MarshalAs(UnmanagedType.LPStruct)] DOCINFO di);
|
||||
|
||||
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||
private static extern bool EndDocPrinter(IntPtr hPrinter);
|
||||
|
||||
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||
private static extern bool StartPagePrinter(IntPtr hPrinter);
|
||||
|
||||
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||
private static extern bool EndPagePrinter(IntPtr hPrinter);
|
||||
|
||||
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
|
||||
private static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, ref int dwWritten);
|
||||
|
||||
private readonly RawPrinter printer;
|
||||
private bool pageStarted = false;
|
||||
private bool disposed = false;
|
||||
|
||||
internal RawDocumentStream(RawPrinter printer, string docName)
|
||||
{
|
||||
if (!printer.canCreateDocument)
|
||||
// Schauen, dass das über den Printer erstellt wird
|
||||
throw new InvalidOperationException();
|
||||
|
||||
this.printer = printer;
|
||||
|
||||
DOCINFO di = new DOCINFO();
|
||||
// Wenn es sich um einen v4-Treiber (XPS-basiert, eingeführt mit
|
||||
// Windows 8) handelt, muss "XPS_PASS" statt "RAW" verwendet werden.
|
||||
// Siehe: http://support.microsoft.com/kb/2779300
|
||||
di.pDataType = printer.xpsDriver ? "XPS_PASS" : "RAW";
|
||||
di.pDocName = docName;
|
||||
|
||||
CheckApiCall(StartDocPrinter(printer.HandlePrinter, 1, di));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Startet eine neue Seite (nur wichtig für den Windows Spooler).
|
||||
/// </summary>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">Beim
|
||||
/// Aufruf einer Win32-API trat ein Fehler auf.</exception>
|
||||
public void StartPage()
|
||||
{
|
||||
if (!pageStarted)
|
||||
{
|
||||
CheckApiCall(StartPagePrinter(printer.HandlePrinter));
|
||||
pageStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Beendet die aktuelle Seite.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">Beim
|
||||
/// Aufruf einer Win32-API trat ein Fehler auf.</exception>
|
||||
public void FinishPage()
|
||||
{
|
||||
if (pageStarted)
|
||||
{
|
||||
CheckApiCall(EndPagePrinter(printer.HandlePrinter));
|
||||
pageStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (pageStarted)
|
||||
FinishPage();
|
||||
|
||||
CheckApiCall(EndDocPrinter(printer.HandlePrinter));
|
||||
|
||||
printer.currentDoc = null;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return false; } }
|
||||
|
||||
public override bool CanSeek { get { return false; } }
|
||||
|
||||
public override bool CanWrite { get { return true; } }
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
public override long Length { get { throw new NotSupportedException(); } }
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sendet die angegebenen Bytes an den Drucker.
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">Beim Aufruf
|
||||
/// einer Win32-API trat ein Fehler auf.</exception>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("RawDocumentStream");
|
||||
|
||||
if (offset < 0 || count < 0 || offset + count > buffer.Length)
|
||||
throw new ArgumentException();
|
||||
|
||||
if (!pageStarted)
|
||||
StartPage();
|
||||
|
||||
// Pointer auf Byte-Array holen.
|
||||
// In C#: fixed (byte* pBytes = buffer) { ... },
|
||||
// dann Pointerarithmetik verwenden
|
||||
GCHandle hgc = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
int bytesCompleted = 0;
|
||||
while (count - bytesCompleted > 0)
|
||||
{
|
||||
int tempBytesWritten = 0;
|
||||
|
||||
CheckApiCall(WritePrinter(printer.HandlePrinter, Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset + bytesCompleted), count - bytesCompleted, ref tempBytesWritten));
|
||||
|
||||
if (!(tempBytesWritten > 0))
|
||||
break;
|
||||
|
||||
bytesCompleted += tempBytesWritten;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
hgc.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user