Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Printing support using Windows spooler #223

Open
sinni800 opened this issue Feb 14, 2023 · 8 comments
Open

Printing support using Windows spooler #223

sinni800 opened this issue Feb 14, 2023 · 8 comments
Labels
enhancement New feature or request

Comments

@sinni800
Copy link

As USB printing is currently not possible and some weird china printers don't seem to really have a good way to make a virtual com port (at least I havent found any) I think a good way to make this possible is to have a "Windows Printer Spooler" printer backend.

This project here (same license, MIT) has such an implementation: https://github.com/mtmsuhail/ESC-POS-USB-NET/blob/master/ESC-POS-USB-NET/Helper/RawPrinterHelper.cs

Essentially, creating a RAW printer in Windows and sending ESCPOS to it like you would to a COM port works just fine like that, but this other library is not nearly as good as this one...

@lukevp
Copy link
Owner

lukevp commented Feb 25, 2023

I'm super interested in enabling native USB printer through the windows spooler! The only criteria I would have are:

  1. library still works on all platforms (it is currently compatible with .net standard and runs on Windows, MacOS, Linux, Xamarin iOS, Xamarin Android). It's fine if the USB module itself only works on Windows for now, as long as the overall library doesn't have to change to be full .NET framework or something like that.
  2. it uses raw printing still even though it's going through the windows spooler.
  3. the usage and setup can be documented in the README.

Is this something you'd be interested in contributing? I think it would be really valuable for a lot of people who can't use the virtual COM, it's just not something I was familiar with on how to implement.

@sinni800
Copy link
Author

sinni800 commented Feb 25, 2023

Hmm, I have hacked together something that works perfectly fine for my things, but I haven't put any junctions in that would properly make it fail on Non-Windows.

Since I have this just as a local class in my application, I'll just paste it here verbatim for now:

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ESCPOS_NET;
using System.Runtime.InteropServices;
using System.Timers;

namespace kasse2 {
    public class WindowsPrinter : BasePrinter {
        private string _printerName;
        private MemoryStream _stream;

        public WindowsPrinter(string printerName) : base() {
            _stream = new MemoryStream();
            Writer = new BinaryWriter(_stream);
            _printerName = printerName;
        }

        public override void Flush(object sender, ElapsedEventArgs e) {
            if (BytesWrittenSinceLastFlush > 0) {
                var bytes = _stream.ToArray();
                _stream = new MemoryStream();
                Writer = new BinaryWriter(_stream);
                SendBytesToPrinter(_printerName, bytes);

            }
            BytesWrittenSinceLastFlush = 0;
        }


        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public class DOCINFOA {
            [MarshalAs(UnmanagedType.LPStr)] public string pDocName;
            [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;
            [MarshalAs(UnmanagedType.LPStr)] public string pDataType;
        }

        #region Declaration Dll

        [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi,
            ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter,
            IntPtr pd);

        [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool ClosePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi,
            ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartDocPrinter(IntPtr hPrinter, int level,
            [In][MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

        [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndDocPrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);

        #endregion

        #region Methods

        // SendBytesToPrinter()
        // When the function is given a printer name and an unmanaged array
        // of bytes, the function sends those bytes to the printer queue.
        // Returns true on success, false on failure.
        public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, int dwCount) {
            int dwError = 0, dwWritten = 0;
            var hPrinter = new IntPtr(0);
            var di = new DOCINFOA();
            var bSuccess = false; // Assume failure unless you specifically succeed.

            di.pDocName = "VIP RAW PrinterDocument";
            di.pDataType = "RAW";

            // Open the printer.
            if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero)) {
                // Start a document.
                if (StartDocPrinter(hPrinter, 1, di)) {
                    // Start a page.
                    if (StartPagePrinter(hPrinter)) {
                        // Write your bytes.
                        bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                        EndPagePrinter(hPrinter);
                    }
                    EndDocPrinter(hPrinter);
                }
                ClosePrinter(hPrinter);
            }
            // If you did not succeed, GetLastError may give more information
            // about why not.
            if (bSuccess == false)
                dwError = Marshal.GetLastWin32Error();

            return bSuccess;
        }

        public static bool SendFileToPrinter(string szPrinterName, string szFileName) {
            // Open the file.
            var fs = new FileStream(szFileName, FileMode.Open);

            // Create a BinaryReader on the file.
            var br = new BinaryReader(fs);

            // Dim an array of bytes big enough to hold the file's contents.
            var bytes = new byte[fs.Length];
            var bSuccess = false;

            // Your unmanaged pointer.
            var pUnmanagedBytes = new IntPtr(0);
            var nLength = Convert.ToInt32(fs.Length);

            // Read the contents of the file into the array.
            bytes = br.ReadBytes(nLength);
            // Allocate some unmanaged memory for those bytes.
            pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
            // Copy the managed byte array into the unmanaged array.
            Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
            // Send the unmanaged bytes to the printer.
            bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
            // Free the unmanaged memory that you allocated earlier.
            Marshal.FreeCoTaskMem(pUnmanagedBytes);
            return bSuccess;
        }

        public static bool SendBytesToPrinter(string szPrinterName, byte[] data) {
            var pUnmanagedBytes = Marshal.AllocCoTaskMem(data.Length); // Allocate unmanaged memory
            Marshal.Copy(data, 0, pUnmanagedBytes, data.Length); // copy bytes into unmanaged memory
            var retval = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, data.Length);
            Marshal.FreeCoTaskMem(pUnmanagedBytes); // Free the allocated unmanaged memory

            return retval;
        }

        public static bool SendStringToPrinter(string szPrinterName, string szString) {
            // How many characters are in the string?
            var dwCount = szString.Length;

            // Assume that the printer is expecting ANSI text, and then convert
            // the string to ANSI text.
            var pBytes = Marshal.StringToCoTaskMemAnsi(szString);

            // Send the converted ANSI string to the printer.
            var result = SendBytesToPrinter(szPrinterName, pBytes, dwCount);
            Marshal.FreeCoTaskMem(pBytes);

            return result;
        }

        //if you want a wrapper function for you strings :
        public static bool SendAsciiToPrinter(string szPrinterName, string data) {
            var retval = false;

            //if  you are using UTF-8 and get wrong values in qrcode printing, you must use ASCII instead.
            //retval = SendBytesToPrinter(szPrinterName, Encoding.UTF8.GetBytes(data));
            retval = SendBytesToPrinter(szPrinterName, Encoding.ASCII.GetBytes(data));

            return retval;
        }

        #endregion
    }
}

This is basically mostly copied from that other project I mentioned, since it uses the same license we can at least talk with full license compatibility here.

All it needs is a Windows printer name and a Windows printer set up to use RAW.

Is something like this to your quality requirements? If yes, I could fork the project and commit this file properly with a readme change

@reislanes
Copy link

I was researching the same thing. @sinni800 have you foked? Or made some personal implementation for your own?
As a fast solution i'm using both libraries, this one for Ethernet or Virtual COM printers, and the other one for USB printers.

@lukevp
Copy link
Owner

lukevp commented Jun 17, 2023

I'm definitely interested in getting changes upstreamed in this repo to allow the windows print spooler, as long as it works well and it follows a similar API to the other printers, and properly indicates to the end user if their platform is not supported. Honestly on MacOS and Linux, USB printers usually mount a file under /dev/ttyUSBx that you can print to without any drivers, so there's already a solution for USB printing from other platforms in most cases, so this could just be solved by documentation and the underlying implementation throwing an exception on Windows.

The problem I see in the code above is that it has implementation-specific methods for sending things to the printer. It would make more sense if the printer name / port / etc. were all set up in the constructor, and you just used it like any other printer implementation (construct an instance and then start writing bytes to it).

@lukevp lukevp added the enhancement New feature or request label Jun 17, 2023
@sinni800
Copy link
Author

They are though, you only need to instantiate WindowsPrinter with a printer Name as the parameter. To print to it, you can use the BasePrinter methods. It could probably be cleaned up a bit, but it already works like that with the current code

@reislanes
Copy link

Yeah, i first used it at my project as Elgin I9 printers use this implementation at the sample code guide to C# printing

The Class Helper i9 has the base code:

Imports System.Text
Imports System.Runtime.InteropServices
Imports System.IO
Namespace HelperI9
Friend Class RawPrinterHelper
' Structure and API declarions:
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Class DOCINFOA
<MarshalAs(UnmanagedType.LPStr)>
Public pDocName As String
<MarshalAs(UnmanagedType.LPStr)>
Public pOutputFile As String
<MarshalAs(UnmanagedType.LPStr)>
Public pDataType As String
End Class

    <DllImport("winspool.Drv", EntryPoint:="OpenPrinterA", SetLastError:=True, CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function OpenPrinter(
    <MarshalAs(UnmanagedType.LPStr)> ByVal szPrinter As String, <Out> ByRef hPrinter As IntPtr, ByVal pd As IntPtr) As Boolean
    End Function

    <DllImport("winspool.Drv", EntryPoint:="ClosePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean
    End Function

    <DllImport("winspool.Drv", EntryPoint:="StartDocPrinterA", SetLastError:=True, CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function StartDocPrinter(ByVal hPrinter As IntPtr, ByVal level As Integer,
    <[In], MarshalAs(UnmanagedType.LPStruct)> ByVal di As DOCINFOA) As Boolean
    End Function

    <DllImport("winspool.Drv", EntryPoint:="EndDocPrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function EndDocPrinter(ByVal hPrinter As IntPtr) As Boolean
    End Function

    <DllImport("winspool.Drv", EntryPoint:="StartPagePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function StartPagePrinter(ByVal hPrinter As IntPtr) As Boolean
    End Function

    <DllImport("winspool.Drv", EntryPoint:="EndPagePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function EndPagePrinter(ByVal hPrinter As IntPtr) As Boolean
    End Function

    <DllImport("winspool.Drv", EntryPoint:="WritePrinter", SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)>
    Public Shared Function WritePrinter(ByVal hPrinter As IntPtr, ByVal pBytes As IntPtr, ByVal dwCount As Integer, <Out> ByRef dwWritten As Integer) As Boolean
    End Function

    ' SendBytesToPrinter()
    ' When the function is given a printer name and an unmanaged array
    ' of bytes, the function sends those bytes to the printer queue.
    ' Returns true on success, false on failure.
    Public Shared Function SendBytesToPrinter(ByVal szPrinterName As String, ByVal pBytes As IntPtr, ByVal dwCount As Integer) As Boolean
        Dim dwError = 0, dwWritten = 0
        Dim hPrinter As IntPtr = New IntPtr(0)
        Dim di As DOCINFOA = New DOCINFOA()
        Dim bSuccess = False ' Assume failure unless you specifically succeed.
        di.pDocName = "i9 RAW Document"
        di.pDataType = "RAW"

        ' Open the printer.
        If OpenPrinter(szPrinterName.Normalize(), hPrinter, IntPtr.Zero) Then
            ' Start a document.
            If StartDocPrinter(hPrinter, 1, di) Then
                ' Start a page.
                If StartPagePrinter(hPrinter) Then
                    ' Write your bytes.
                    bSuccess = WritePrinter(hPrinter, pBytes, dwCount, dwWritten)
                    EndPagePrinter(hPrinter)
                End If

                EndDocPrinter(hPrinter)
            End If

            ClosePrinter(hPrinter)
        End If
        ' If you did not succeed, GetLastError may give more information
        ' about why not.
        If bSuccess = False Then
            dwError = Marshal.GetLastWin32Error()
        End If

        Return bSuccess
    End Function

    Public Shared Function SendFileToPrinter(ByVal szPrinterName As String, ByVal szFileName As String) As Boolean
        ' Open the file.
        Dim fs As FileStream = New FileStream(szFileName, FileMode.Open)
        ' Create a BinaryReader on the file.
        Dim br As BinaryReader = New BinaryReader(fs)
        ' Dim an array of bytes big enough to hold the file's contents.
        Dim bytes = New Byte(fs.Length - 1) {}
        Dim bSuccess = False
        ' Your unmanaged pointer.
        Dim pUnmanagedBytes As IntPtr = New IntPtr(0)
        Dim nLength As Integer
        nLength = Convert.ToInt32(fs.Length)
        ' Read the contents of the file into the array.
        bytes = br.ReadBytes(nLength)
        ' Allocate some unmanaged memory for those bytes.
        pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength)
        ' Copy the managed byte array into the unmanaged array.
        Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength)
        ' Send the unmanaged bytes to the printer.
        bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength)
        ' Free the unmanaged memory that you allocated earlier.
        Marshal.FreeCoTaskMem(pUnmanagedBytes)
        Return bSuccess
    End Function

    Public Shared Function SendBytesToPrinter(ByVal szPrinterName As String, ByVal data As Byte()) As Boolean
        Dim retval = False
        Dim pUnmanagedBytes = Marshal.AllocCoTaskMem(data.Length) ' Allocate unmanaged memory
        Marshal.Copy(data, 0, pUnmanagedBytes, data.Length) ' copy bytes into unmanaged memory
        retval = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, data.Length) 'Send bytes to printer
        Marshal.FreeCoTaskMem(pUnmanagedBytes) ' Free the allocated unmanaged memory
        Return retval
    End Function

    Public Shared Function SendStringToPrinter(ByVal szPrinterName As String, ByVal szString As String) As Boolean
        Dim pBytes As IntPtr
        Dim dwCount As Integer
        ' How many characters are in the string?
        dwCount = szString.Length
        ' Assume that the printer is expecting ANSI text, and then convert
        ' the string to ANSI text.
        pBytes = Marshal.StringToCoTaskMemAnsi(szString)
        ' Send the converted ANSI string to the printer.
        SendBytesToPrinter(szPrinterName, pBytes, dwCount)
        Marshal.FreeCoTaskMem(pBytes)
        Return True
    End Function

    'if you want a wrapper function for you strings :
    Public Shared Function SendASCiiToPrinter(ByVal szPrinterName As String, ByVal data As String) As Boolean
        Dim retval = False

        'if  you are using UTF-8 and get wrong values in qrcode printing, you must use ASCII instead.
        'retval = SendBytesToPrinter(szPrinterName, Encoding.UTF8.GetBytes(data));


        retval = SendBytesToPrinter(szPrinterName, Encoding.ASCII.GetBytes(data))
        Return retval
    End Function
End Class

End Namespace

@reislanes
Copy link

Imports System.Text
Public Class EscPos_i9

'if you want to use decimal values in your methods.
Public Function intTobyte(ByVal data As Integer()) As Byte()
    Dim byteData As Byte() = data.[Select](Function(x) CByte(x)).ToArray() ' coonvert int array to byte
    Return byteData
End Function



'initialize printer

'ESC @
Public Sub initializePrinter(ByVal szPrinterName As String)
    Dim command = {27, 64}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(command))
End Sub
'print text

Public Function printText(ByVal szPrinterName As String, ByVal data As String) As Boolean
    'for more character sets: http://www.ascii-codes.com/

    'for another charsets: 
    'Encoding ascii = Encoding.GetEncoding("ascii");
    'Encoding windows = Encoding.GetEncoding("Windows-1252");

    'you must use this encoding  for  brazilian portuguese special characters: ^,~,ç,á...
    Dim brazilian = Encoding.GetEncoding("IBM860")
    Dim byteData = brazilian.GetBytes(data)
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, byteData)
    Return True
End Function

' Print Position Commands
'ESC a n
Public Function SelectJustification(ByVal szPrinterName As String, ByVal justification_code As Integer) As Boolean

    '0= default 
    '48 left
    '1,49 centering
    '2,50 right

    Dim align = {27, 97, justification_code}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(align))
    Return True
End Function

'Character Control Commands

'use this mode to cancel another mode.
Public Function normalModeText(ByVal szPrinterName As String) As Boolean
    Dim normal = {27, 33, 0}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(normal))
    Return True
End Function

'Character font A (12 × 24) selected.
Public Function charFontAText(ByVal szPrinterName As String) As Boolean
    Dim fontA = {27, 33, 0}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(fontA))
    Return True
End Function

'Character font B (9 × 17) selected.
Public Function charFontBText(ByVal szPrinterName As String) As Boolean
    Dim fontB = {27, 33, 1}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(fontB))
    Return True
End Function

'Emphasized mode is turned on
Public Function emphasizedModeText(ByVal szPrinterName As String) As Boolean
    Dim mode = {27, 33, 8}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(mode))
    Return True
End Function

'Double-height selected.
Public Function doubleHeightText(ByVal szPrinterName As String) As Boolean
    Dim height = {27, 33, 16}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(height))
    Return True
End Function
'Double-width selected.
Public Function DoubleWidthText(ByVal szPrinterName As String) As Boolean
    Dim width = {27, 33, 32}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(width))
    Return True
End Function


'Underline mode is turned on
Public Function UnderlineModeText(ByVal szPrinterName As String) As Boolean
    Dim underline = {27, 33, 128}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(underline))
    Return True
End Function


'print and Line feed
Public Function lineFeed(ByVal szPrinterName As String, ByVal numLines As Integer) As Boolean
    ' fucntion LF 
    Dim lf = {10}

    For i = 1 To numLines
        HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(lf))
    Next

    Return True
End Function

'Generate pulse in Real Time
Public Function drawerKick(ByVal szPrinterName As String) As Boolean
    ' function DLE DC4 fn m t (fn=1)


    Dim pulse = {27, 112, 0, 100, 200}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(pulse))
    Return True
End Function

'execute test print
Public Function testPrint(ByVal szPrinterName As String) As Boolean
    'function GS ( A pL pH n m

    Dim test = {29, 40, 65, 2, 0, 0, 2}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(test))
    Return True
End Function


'Select an international character set
Public Function charSet(ByVal szPrinterName As String, ByVal language As Integer) As Boolean
    'function ESC R n
    '0-USA
    '12-Latin America
    '
    Dim char_set = {27, 82, language}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(char_set))
    Return True
End Function


'select character code table
Public Function codeTable(ByVal szPrinterName As String, ByVal language As Integer) As Boolean
    'function Esc t n
    ' 0 - PC437 (USA: Standard Europe)]
    ' 40 [ISO8859-15 (Latin9)]
    ' 3 [PC860 (Portuguese)]

    Dim code = {27, 116, language}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(code))
    Return True
End Function

'Select cut mode and cut paper
Public Function CutPaper(ByVal szPrinterName As String) As Boolean
    'hex 1D 56 m, m =0,1,48,49

    Dim cut = {29, 86, 0}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(cut))
    Return True
End Function


'activate printer buzzer
Public Function buzzer(ByVal szPrinterName As String) As Boolean



    'hex data = "1b 28 41 05 00 61 64 03 0a 0a";
    Dim lBuzzer = {27, 40, 65, 5, 0, 97, 100, 3, 10, 10}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(lBuzzer))
    ' RawPrinterHelper.SendASCiiToPrinter(szPrinterName, data);
    Return True
End Function

'*************************** barcode  commands **********************************

'GS h n - sets bar the height of bar code to n dots.
Public Function barcode_height(ByVal szPrinterName As String, ByVal Optional range As Integer = 162) As Boolean ' default = 162

    'range 1 ≤ n ≤ 255
    Dim height = {29, 104, range}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(height))
    Return True
End Function

' GS w n Set bar code width
Public Function barcode_width(ByVal szPrinterName As String, ByVal Optional range As Integer = 3) As Boolean
    'range = 2 ≤ n ≤ 6
    Dim width = {29, 119, range}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(width))
    Return True
End Function

'GS f n Select a font for the HRI characters when printing a bar code.
Public Function barcodeHRI_chars(ByVal szPrinterName As String, ByVal Optional font_code As Integer = 0) As Boolean 'default 0 

    '[Range] n = 0, 1, 48, 49
    Dim hri = {29, 102, font_code}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(hri))
    Return True
End Function


'GS H n Select print position of HRI characters
Public Function barcodeHRIPostion(ByVal szPrinterName As String, ByVal Optional position_code As Integer = 1) As Boolean 'default = 0

    '[Range] 0 ≤ n ≤ 3, 48 ≤ n ≤ 51 

    Dim print_position = {29, 72, position_code}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(print_position))
    Return True
End Function

'GS k Print barcode
'<Function A>
Public Function printBarcode(ByVal szPrinterName As String, ByVal data As String, ByVal Optional type As Integer = 2) As Boolean 'for this example 2 = JAN/EAN13
    Dim barcode = {29, 107, type}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(barcode))
    HelperI9.RawPrinterHelper.SendStringToPrinter(szPrinterName, data)
    Dim nul = {0} ' null char at the end.
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(nul))
    Return True
End Function

'GS k Print Barcode
' <Function B>
Public Function printBarcodeB(ByVal szPrinterName As String, ByVal data As String, ByVal Optional type As Integer = 73) As Boolean 'for this example 73 = CODE128
    Dim size = data.Length '  the number of bytes of bar code data
    Dim barcode = {29, 107, type, size}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(barcode))
    HelperI9.RawPrinterHelper.SendStringToPrinter(szPrinterName, data)
    Return True
End Function

'*************************** barcode  commands **********************************



'function to print Qrcode
Public Function printQrcode(ByVal Strdata As String, ByVal szPrinterName As String) As Boolean
    Dim length = Strdata.Length + 3 '  string size  + 3
    'int length = Strdata.Length; 
    Dim length_low_byte As Byte = 0, length_high_byte As Byte = 0
    length_low_byte = CByte(length And &HFF) 'low byte used in function 180 
    length_high_byte = CByte(length >> 8 And &HFF) 'high byte in function 180 


    'if you don't want to use shift operator:
    'int length_low_byte = length % 256;
    'int length_high_byte = length / 256;


    initializePrinter(szPrinterName)

    '<Function ESC a n> Select justification  
    Dim escAn = {27, 97, 0}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(escAn))



    '<Function GS L> Set left margin
    Dim fGsl = {29, 76, 0, 0}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(fGsl))

    '<Function 165> GS ( k p L p H cn fn n (cn = 49,fn = 65)  QR Code: Select the model
    Dim f165 = {29, 40, 107, 4, 0, 49, 65, 50, 0}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(f165))


    '<Function 167> GS ( k pL pH cn fn n (cn = 49, fn = 67) QR Code: Set the size of module
    Dim f167 = {29, 40, 107, 3, 0, 49, 67, 4} '  size of qrcode:  1-16
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(f167))



    '<Function 169> GS ( k pL pH cn fn n (cn = 49, fn = 69) QR Code: Select the error correction level
    Dim f169 = {29, 40, 107, 3, 0, 49, 69, 48}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(f169))


    '<Function 180> GS ( k pL pH cn fn m d1…dk (cn = 49, fn = 80) QR Code: Store the data in the symbol storage area
    'pL and pH are the low- and high-order bytes of a 16-bit integer value that specifies the length in bytes of the following data  

    Dim f180 = {29, 40, 107, length_low_byte, length_high_byte, 49, 80, 48}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(f180))



    'send string/url to printer
    'RawPrinterHelper.SendASCiiToPrinter(szPrinterName, Strdata);
    HelperI9.RawPrinterHelper.SendStringToPrinter(szPrinterName, Strdata)

    '<Function 181> GS ( k pL pH cn fn m (cn = 49, fn = 81) QR Code: Print the symbol data in the symbol storage area
    Dim f181 = {29, 40, 107, 3, 0, 49, 81, 48}
    HelperI9.RawPrinterHelper.SendBytesToPrinter(szPrinterName, intTobyte(f181))

    '

    Return True
End Function

End Class

Gary-gr9 added a commit to Gary-gr9/ESC-POS-.NET that referenced this issue Jul 6, 2023
…linux as well) with complete printer status report back.

No driver install or Virtual Serial Port needed.
Choose printer from list of usb devices and send data straight to the printer like Serial or Network printer.
Possible fix for Isuues : lukevp#186 lukevp#220 lukevp#223
@matthiasvd94
Copy link

I have searched a lot to find a compatible COM port emulation and for me the Epson TM Virtual Port Driver is the only one that works properly for every chinese POS printer.
The standard driver for the device is a POS-58 driver which will not create a virtual COM but only a USB001 for example which cannot be used.
So in fact if you use the Epson TM you can connect every printer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants