├── .npmignore ├── ESC_POS.png ├── ESCPOS_Command_Manual.pdf ├── Dockerfile ├── docker-compose.yml ├── azure-pipelines.yml ├── ESCPOSTest ├── ESCPOSTest.csproj ├── StringDiffHelper.cs └── UnitTest.cs ├── .devcontainer ├── devcontainer.json └── Dockerfile ├── ESCPOS ├── LICENSE ├── ESCPOS.csproj ├── Utils │ ├── Utils.cs │ └── Enums.cs ├── Printer.cs └── ESCPOSCommands │ ├── SimpleCommands.cs │ └── Commands.cs ├── ESCPOS.sln ├── CHANGELOG.md ├── .gitignore └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | README.md 2 | test-project 3 | .vscode 4 | .npmignore 5 | -------------------------------------------------------------------------------- /ESC_POS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorocampos/ESCPOS/HEAD/ESC_POS.png -------------------------------------------------------------------------------- /ESCPOS_Command_Manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorocampos/ESCPOS/HEAD/ESCPOS_Command_Manual.pdf -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env 2 | WORKDIR /app 3 | COPY . . 4 | WORKDIR /app 5 | RUN dotnet publish ./ESCPOS/ESCPOS.csproj -c Release 6 | VOLUME ["/output"]] 7 | COPY ./ESCPOS/bin/Debug/netstandard2.0 /output 8 | #RUN ["dotnet","test"] 9 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | escpos: 4 | image: escpos 5 | container_name: escpos_compose 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | volumes: 10 | - /output:/app/ESCPOS/bin/Debug/netstandard2.0/ 11 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | 4 | pool: 5 | vmImage: 'ubuntu-latest' 6 | 7 | variables: 8 | buildConfiguration: 'Release' 9 | 10 | steps: 11 | - task: DotNetCoreCLI@2 12 | displayName: Build 13 | inputs: 14 | command: build 15 | projects: '**/*.csproj' 16 | - task: DotNetCoreCLI@2 17 | inputs: 18 | command: test 19 | projects: '**/*Test/*.csproj' 20 | arguments: '--configuration $(buildConfiguration)' 21 | -------------------------------------------------------------------------------- /ESCPOSTest/ESCPOSTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | true 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Azure Functions & C# - .NET 6 (Isolated)", 3 | "dockerFile": "Dockerfile", 4 | "forwardPorts": [ 7071 ], 5 | 6 | // Configure tool-specific properties. 7 | "customizations": { 8 | // Configure properties specific to VS Code. 9 | "vscode": { 10 | // Add the IDs of extensions you want installed when the container is created. 11 | "extensions": [ 12 | "ms-dotnettools.csharp" 13 | ] 14 | } 15 | }, 16 | 17 | // Use 'postCreateCommand' to run commands after the container is created. 18 | // "postCreateCommand": "dotnet restore", 19 | 20 | // Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 21 | "remoteUser": "vscode", 22 | "features": { 23 | "ghcr.io/devcontainers/features/dotnet:1": {} 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Find the Dockerfile at this URL 2 | # https://github.com/Azure/azure-functions-docker/blob/dev/host/4/bullseye/amd64/dotnet/dotnet-isolated/dotnet-isolated-core-tools.Dockerfile 3 | FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated6.0-core-tools 4 | 5 | # Uncomment following lines If you want to enable Development Container Script 6 | # For more details https://github.com/microsoft/vscode-dev-containers/tree/main/script-library 7 | 8 | # Avoid warnings by switching to noninteractive 9 | # ENV DEBIAN_FRONTEND=noninteractive 10 | 11 | # # Comment out these lines if you want to use zsh. 12 | 13 | # ARG INSTALL_ZSH=true 14 | # ARG USERNAME=vscode 15 | # ARG USER_UID=1000 16 | # ARG USER_GID=$USER_UID 17 | 18 | # RUN apt-get update && curl -ssL https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/common-debian.sh -o /tmp/common-script.sh \ 19 | # && /bin/bash /tmp/common-script.sh "$INSTALL_ZSH" "$USERNAME" "$USER_UID" "$USER_GID" \ 20 | # && rm /tmp/common-script.sh -------------------------------------------------------------------------------- /ESCPOS/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Igor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ESCPOS.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29215.179 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ESCPOS", "ESCPOS\ESCPOS.csproj", "{69B133DE-222B-4061-BC13-CE345CF176FD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ESCPOSTest", "ESCPOSTest\ESCPOSTest.csproj", "{A4759F20-0E7D-41A5-B0C0-239FED8D9383}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {69B133DE-222B-4061-BC13-CE345CF176FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {69B133DE-222B-4061-BC13-CE345CF176FD}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {69B133DE-222B-4061-BC13-CE345CF176FD}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {69B133DE-222B-4061-BC13-CE345CF176FD}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {A4759F20-0E7D-41A5-B0C0-239FED8D9383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {A4759F20-0E7D-41A5-B0C0-239FED8D9383}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {A4759F20-0E7D-41A5-B0C0-239FED8D9383}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {A4759F20-0E7D-41A5-B0C0-239FED8D9383}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {0ADE0FC4-C99F-477D-B34E-5B3EBA62DAA1} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /ESCPOS/ESCPOS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | Igor Campos 6 | A .NET Standard 2.0 ESC/POS Printer Commands Helper 7 | https://github.com/igorocampos/ESCPOS 8 | https://github.com/igorocampos/ESCPOS 9 | true 10 | 11 | true 12 | LICENSE 13 | 1.3.0.0 14 | 1.3.0.0 15 | 1.3.0 16 | ESC_POS.png 17 | http://campos.cf/ESC_POS.png 18 | escpos;esc;pos;receipt;thermal;printer;esc/pos;sat;cfe 19 | true 20 | true 21 | true 22 | snupkg 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | True 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v1.3.0 2 | - Methods PrintBarcode and PrintQRCode are obsolete and will thow error if used (see #29) 3 | - Updated Tests to .NET 8 4 | - Printer class added, so things like Column and Encoding can be set and leveraged 5 | 6 | # v1.2.4 7 | - Renamed methods PrintBarcode and PrintQRCode and marked old ones as obsolete (see #29) 8 | - Added support for any Encoding instead of just UTF-8 9 | - Added Simple Commands as alias of existing ones (see #30) 10 | - Created Extension Methods for Barcode and QRCode (see #31) 11 | 12 | # v1.2.3 13 | - Added new optional parameter for Barcode command (see [#26](https://github.com/igorocampos/ESCPOS/issues/26)). 14 | - Added support for GitHub Codespaces 15 | - Added Dockerfile and docker-compose to the project 16 | - Updated Tests to .NET 6 17 | 18 | # v1.2.2 19 | - Fixed SourceLink. 20 | - Added new unit tests for extension method `Add`. 21 | 22 | # v1.2.1 23 | - Enabled SourceLink. 24 | 25 | # v1.2.0 26 | - New Utility overload for extension method `Add`, accepting string parameters instead of byte arrays. This will prevent a lot of `.ToBytes()` in the code. 27 | - New `SelectCharSize` method that combines Width and Height size of characters (see [#10](https://github.com/igorocampos/ESCPOS/issues/10)). 28 | 29 | # v1.1.2 30 | - Bug fixed in `PrintBarCode` method when using CODE128 barcodes (see [#9](https://github.com/igorocampos/ESCPOS/issues/9)). 31 | 32 | # v1.1.1 33 | - `Print` method now allows a network address (host:port) as a printer address. 34 | 35 | # v1.1.0 36 | - Moved `Print` method to `Commands.cs`. 37 | - Moved all enums to `Enums.cs`. 38 | 39 | # v1.0.3 40 | - No relevant changed. 41 | - `AssemblyVersion` was corrected. 42 | 43 | # v1.0.2 44 | - Renamed `QRCodeCorrection` enum. 45 | - Created `Add` extension method for multiple strings as parameters. 46 | - `PrintQRCode` method now has default values in all parameters but content. 47 | - Created `Print` extension method for byte array that sends data to a printer address. 48 | - Created `ToBytes` extension method to convert UTF-8 strings into a byte array. 49 | - Add Unit Tests project. 50 | 51 | # v1.0.1 52 | - First functional version. 53 | - No Test Project is available yet. 54 | -------------------------------------------------------------------------------- /ESCPOS/Utils/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ESCPOS.Utils 5 | { 6 | public static class Utils 7 | { 8 | private static byte[] Add(this byte[] array1, byte[] array2) 9 | { 10 | if (array1 is null) 11 | return array2; 12 | 13 | if (array2 is null) 14 | return array1; 15 | 16 | byte[] result = new byte[array1.Length + array2.Length]; 17 | Array.Copy(array1, result, array1.Length); 18 | Array.Copy(array2, 0, result, array1.Length, array2.Length); 19 | return result; 20 | } 21 | 22 | public static byte[] Add(this byte[] array1, params object[] objects) 23 | { 24 | foreach (var obj in objects) 25 | { 26 | if (obj is byte[] array2) 27 | array1 = array1.Add(array2); 28 | else if (obj is string str) 29 | array1 = array1.Add(str.ToBytes()); 30 | } 31 | return array1; 32 | } 33 | 34 | public static byte[] Add(this byte[] array1, Encoding encoding, params object[] objects) 35 | { 36 | foreach (var obj in objects) 37 | { 38 | if (obj is byte[] array2) 39 | array1 = array1.Add(array2); 40 | else if (obj is string str) 41 | array1 = array1.Add(str.ToBytes(encoding)); 42 | } 43 | return array1; 44 | } 45 | 46 | 47 | public static byte[] Add(this byte[] array, params string[] strings) 48 | { 49 | StringBuilder sb = new StringBuilder(); 50 | foreach (string str in strings) 51 | sb.Append(str); 52 | 53 | return array.Add(sb.ToString().ToBytes()); 54 | } 55 | 56 | public static byte[] Add(this byte[] array, Encoding encoding, params string[] strings) 57 | { 58 | StringBuilder sb = new StringBuilder(); 59 | foreach (string str in strings) 60 | sb.Append(str); 61 | 62 | return array.Add(sb.ToString().ToBytes(encoding)); 63 | } 64 | 65 | public static byte[] Add(this byte[] array1, params byte[][] arrays) 66 | { 67 | foreach (byte[] array2 in arrays) 68 | array1 = array1.Add(array2); 69 | 70 | return array1; 71 | } 72 | 73 | public static byte[] ToBytes(this string source, Encoding encoding = null) 74 | => (encoding ?? Encoding.UTF8).GetBytes(source); 75 | 76 | public static string ToUTF8(this byte[] array) 77 | => Encoding.UTF8.GetString(array); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ESCPOSTest/StringDiffHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Globalization; 4 | using System.IO; 5 | 6 | namespace ESCPOSTest 7 | { 8 | //For more information, please see https://haacked.com/archive/2012/01/14/comparing-strings-in-unit-tests.aspx/ 9 | public static class StringDiffHelper 10 | { 11 | public static void ShouldEqualWithDiff(string actualValue, string expectedValue, DiffStyle diffStyle = DiffStyle.Full) 12 | => ShouldEqualWithDiff(actualValue, expectedValue, diffStyle, Console.Out); 13 | 14 | public static void ShouldEqualWithDiff(string actualValue, string expectedValue, DiffStyle diffStyle, TextWriter output) 15 | { 16 | if (actualValue is null || expectedValue is null) 17 | { 18 | Assert.AreEqual(expectedValue, actualValue); 19 | return; 20 | } 21 | 22 | if (actualValue.Equals(expectedValue, StringComparison.Ordinal)) 23 | return; 24 | 25 | output.WriteLine(" Idx Expected Actual"); 26 | output.WriteLine("-------------------------"); 27 | int maxLen = Math.Max(actualValue.Length, expectedValue.Length); 28 | int minLen = Math.Min(actualValue.Length, expectedValue.Length); 29 | for (int i = 0; i < maxLen; i++) 30 | if (diffStyle != DiffStyle.Minimal || i >= minLen || actualValue[i] != expectedValue[i]) 31 | // put a mark beside a differing row index character decimal value character safe string character decimal value character safe string 32 | output.WriteLine($"{(i < minLen && actualValue[i] == expectedValue[i] ? " " : " * ")} {i,-3} {(i < expectedValue.Length ? ((int)expectedValue[i]).ToString() : ""),-4} {(i < expectedValue.Length ? expectedValue[i].ToSafeString() : ""),-3} {(i < actualValue.Length ? ((int)actualValue[i]).ToString() : ""),-4} {(i < actualValue.Length ? actualValue[i].ToSafeString() : ""),-3}"); 33 | 34 | output.WriteLine(); 35 | Assert.AreEqual(expectedValue, actualValue); 36 | } 37 | 38 | private static string ToSafeString(this char c) 39 | { 40 | if (char.IsControl(c) || char.IsWhiteSpace(c)) 41 | switch (c) 42 | { 43 | case '\r': return @"\r"; 44 | case '\n': return @"\n"; 45 | case '\t': return @"\t"; 46 | case '\a': return @"\a"; 47 | case '\v': return @"\v"; 48 | case '\f': return @"\f"; 49 | default: return $"\\u{(int)c:X};"; 50 | } 51 | 52 | return c.ToString(CultureInfo.InvariantCulture); 53 | } 54 | } 55 | 56 | public enum DiffStyle 57 | { 58 | Full, 59 | Minimal 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ESCPOS/Printer.cs: -------------------------------------------------------------------------------- 1 | using ESCPOS.Utils; 2 | using System; 3 | using System.Text; 4 | 5 | namespace ESCPOS 6 | { 7 | public class Printer 8 | { 9 | public Encoding Encoding { get; set; } 10 | public int Columns { get; set; } = 42; 11 | public string Address { get; set; } 12 | public byte[] CommandCache { get; set; } 13 | 14 | public byte[] HorizontalLine => "".PadLeft(Columns, '-').ToBytes(Encoding); 15 | 16 | public byte[] HorizontalDoubleLine => "".PadLeft(Columns, '=').ToBytes(Encoding); 17 | 18 | /// 19 | /// Based on printer's column count provides a byte array for 1 full line where both texts fit, first one aligned to the left and the other to the right. 20 | /// If total length of both texts is greater than printer's column count, Left Aligned text will get truncated to fit both and a whitespace will be added in between 21 | /// Printer will have Text Alignment set to the Left afterwards 22 | /// 23 | /// Text that will be aligned to the Left 24 | /// Text that will be aligned to the Right 25 | /// Byte array that contains a full line with one text aligned to the left and the other to the right 26 | public byte[] SameLineLeftAndRightAlignedText(string leftAlignedText, string rightAlignedText) 27 | { 28 | int totalLength = leftAlignedText.Length + rightAlignedText.Length; 29 | if (rightAlignedText.Length > Columns) 30 | throw new ArgumentException($"Length of Right-Aligned text ({rightAlignedText.Length}) surpass the printer's column count ({Columns})."); 31 | 32 | if (rightAlignedText.Length >= Columns -1) 33 | return Commands.AlignToRight.Add(rightAlignedText.ToBytes(Encoding), Commands.AlignToLeft); 34 | 35 | string result; 36 | if (totalLength >= Columns) 37 | result = $"{leftAlignedText.Substring(0, Columns - (rightAlignedText.Length + 1))} {rightAlignedText}"; 38 | else 39 | { 40 | int padCount = Columns - totalLength; 41 | result = $"{leftAlignedText}{rightAlignedText.PadLeft(padCount + rightAlignedText.Length, ' ')}"; 42 | } 43 | return result.ToBytes(Encoding).Add(Commands.AlignToLeft); 44 | } 45 | 46 | /// 47 | /// Clears the CommandCache for this printer instance. 48 | /// 49 | public void ClearCache() 50 | => CommandCache = null; 51 | 52 | public void AddToCache(params object[] objects) 53 | => CommandCache = CommandCache.Add(Encoding, objects); 54 | 55 | /// 56 | /// Sends content of CommandCache to the set Address for this printer. Once that's done CommandCache will be cleared. 57 | /// Printer Address can't be null empty, or in an unexpected format. 58 | /// 59 | /// 60 | /// is null, empty, or in an unexpected format. 61 | public void Print() 62 | { 63 | if (CommandCache is null) 64 | return; 65 | 66 | try 67 | { 68 | CommandCache.Print(Address); 69 | } 70 | catch (Exception ex) when (ex is ArgumentNullException || ex is ArgumentException) 71 | { 72 | throw new InvalidOperationException("Printer Address can't be null empty, or in an unexpected format.", ex); 73 | } 74 | ClearCache(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ESCPOS/Utils/Enums.cs: -------------------------------------------------------------------------------- 1 | namespace ESCPOS 2 | { 3 | public enum OnOff 4 | { 5 | Off, 6 | On 7 | } 8 | 9 | public enum QRCodeModel 10 | { 11 | Model1 = 49, 12 | Model2 13 | } 14 | 15 | public enum QRCodeCorrection 16 | { 17 | Percent7 = 48, 18 | Percent15, 19 | Percent25, 20 | Percent30 21 | } 22 | 23 | public enum QRCodeSize 24 | { 25 | Tiny = 2, 26 | Small, 27 | Normal, 28 | Large 29 | } 30 | 31 | public enum Justification 32 | { 33 | Left, 34 | Center, 35 | Right 36 | } 37 | 38 | public enum BarCodeType 39 | { 40 | UPC_A = 65, 41 | UPC_E, 42 | EAN13, 43 | EAN8, 44 | CODE39, 45 | ITF, 46 | CODABAR, 47 | CODE93, 48 | CODE128 49 | } 50 | 51 | public enum HRIPosition 52 | { 53 | NotPrinted, 54 | AboveBarcode, 55 | BelowBarcode, 56 | BothAboveAndBelow 57 | } 58 | 59 | public enum CharSizeHeight 60 | { 61 | Normal, 62 | Double, 63 | Triple, 64 | Quadruple, 65 | Quintuple, 66 | Sextuple, 67 | Septuple, 68 | Octuple 69 | } 70 | 71 | #pragma warning disable CA1027 // Mark enums with FlagsAttribute 72 | public enum CharSizeWidth 73 | #pragma warning restore CA1027 // Mark enums with FlagsAttribute 74 | { 75 | Normal, 76 | Double = 16, 77 | Triple = 32, 78 | Quadruple = 48, 79 | Quintuple = 64, 80 | Sextuple = 80, 81 | Septuple = 96, 82 | Octuple = 112 83 | } 84 | 85 | public enum ClockwiseDirection 86 | { 87 | Counterclockwise, 88 | Clockwise 89 | } 90 | 91 | public enum Direction 92 | { 93 | LeftToRight, 94 | BottomToTop, 95 | RightToLeft, 96 | TopToBottom 97 | } 98 | 99 | public enum CharSet 100 | { 101 | USA, 102 | France, 103 | Germany, 104 | UK, 105 | DenmarkI, 106 | Sweden, 107 | Italy, 108 | SpainI, 109 | Japan, 110 | Norway, 111 | DenmarkII, 112 | SpainII, 113 | LatinAmerica 114 | } 115 | 116 | public enum CodeTable 117 | { 118 | USA, 119 | Katakana, 120 | Multilingual, 121 | Portuguese, 122 | CanadianFrench, 123 | Nordic, 124 | Windows1252 = 16, 125 | Cyrillic, 126 | Latin2, 127 | OEM858, 128 | SpacePage = 255 129 | } 130 | 131 | public enum Font 132 | { 133 | A, 134 | B 135 | } 136 | 137 | #pragma warning disable CA1027 // Mark enums with FlagsAttribute 138 | public enum PrintMode 139 | #pragma warning restore CA1027 // Mark enums with FlagsAttribute 140 | { 141 | Reset, 142 | FontB, 143 | EmphasizedOn = 8, 144 | DoubleHeight = 16, 145 | DoubleWidth = 32, 146 | UnderlineOn = 128 147 | } 148 | 149 | public enum UnderlineMode 150 | { 151 | Off, 152 | OneDotThick, 153 | TwoDotsThick 154 | } 155 | 156 | #pragma warning disable CA1027 // Mark enums with FlagsAttribute 157 | public enum LineSpacing 158 | #pragma warning restore CA1027 // Mark enums with FlagsAttribute 159 | { 160 | Default = 2, 161 | Double = 4, 162 | Triple = 6 163 | } 164 | 165 | public enum BarcodeWidth 166 | { 167 | VeryThin = 1, 168 | Thin, 169 | Normal, 170 | Thick, 171 | VeryThick, 172 | Thickest 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /ESCPOS/ESCPOSCommands/SimpleCommands.cs: -------------------------------------------------------------------------------- 1 | namespace ESCPOS 2 | { 3 | public static partial class Commands 4 | { 5 | /// 6 | /// Moves the print position to the next tab position. 7 | /// 8 | /// 9 | /// ·This command is ignored unless the next tab position has been set. 10 | /// ·If the next horizontal tab position exceeds the printing area, the printer sets the printing position to[Printing area width + 1]. 11 | /// ·If this command is received when the printing position is at [printing area width +1], the printer executes print buffer-full printing of the current line and horizontal tab processing from the beginning of the next line. 12 | /// ·The default setting of the horizontal tab position for the paper roll is font A (12 x 24) every 8th character(9th, 17th, 25th, … column). 13 | /// 14 | public static byte[] HorizontalTab => new byte[] { 0x09 }; 15 | 16 | /// 17 | /// Prints the data in the print buffer and feeds one line based on the current line spacing. 18 | /// 19 | /// 20 | /// ·This command sets the print position to the beginning of the line. 21 | /// 22 | public static byte[] LineFeed => new byte[] { 0x0A }; 23 | 24 | /// 25 | /// When automatic line feed is enabled, this command functions the same as LF, when automatic line feed is disabled, this command is ignored 26 | /// 27 | /// 28 | /// ·Sets the print starting position to the beginning of the line. 29 | /// ·The automatic line feed is ignored. 30 | /// 31 | public static byte[] CarriageReturn => new byte[] { 0x0D }; 32 | 33 | /// 34 | /// Prints the data in the print buffer and returns to standard mode. 35 | /// 36 | /// 37 | /// ·The buffer data is deleted after being printed. 38 | /// ·The printer does not execute paper cutting. 39 | /// ·This command sets the print position to the beginning of the line. 40 | /// ·This command is enabled only in page mode. 41 | /// 42 | public static byte[] PrintAndReturnToStandardMode => new byte[] { 0x0C }; 43 | 44 | /// 45 | /// In page mode, delete all the print data in the current printable area. 46 | /// 47 | /// 48 | /// ·This command is enabled only in page mode. 49 | /// ·If data that existed in the previously specified printable area also exists in the currently specified printable area, it is deleted. 50 | /// 51 | public static byte[] CancelPrint => new byte[] { 0x18 }; 52 | 53 | /// 54 | /// ESC @ 55 | /// 56 | public static byte[] InitializePrinter => new byte[] { 0x1B, 0x40 }; 57 | 58 | /// 59 | /// ESC p m t1 t2 60 | /// 61 | public static byte[] OpenDrawer => new byte[] { 0x1B, 0x70, 0x00, 0x3C, 0x78 }; 62 | 63 | /// 64 | /// ESC m 65 | /// 66 | public static byte[] PaperCut => new byte[] { 0x1B, 0x6D }; 67 | 68 | /// 69 | /// ESC i 70 | /// 71 | public static byte[] FullPaperCut => new byte[] { 0x1B, 0x69 }; 72 | 73 | //Alias 74 | public static byte[] HT => HorizontalTab; 75 | public static byte[] LF => LineFeed; 76 | public static byte[] CR => CarriageReturn; 77 | public static byte[] FF => PrintAndReturnToStandardMode; 78 | public static byte[] CAN => CancelPrint; 79 | 80 | public static byte[] PrintModeReset => SelectPrintMode(PrintMode.Reset); 81 | public static byte[] PrintModeFontB => SelectPrintMode(PrintMode.FontB); 82 | public static byte[] PrintModeEmphasis => SelectPrintMode(PrintMode.EmphasizedOn); 83 | 84 | public static byte[] UnderlineModeOff => SelectUnderlineMode(UnderlineMode.Off); 85 | public static byte[] UnderlineModeOn => SelectUnderlineMode(UnderlineMode.OneDotThick); 86 | 87 | public static byte[] DoubleStrikeOn => DoubleStrike(OnOff.On); 88 | public static byte[] DoubleStrikeOff => DoubleStrike(OnOff.Off); 89 | 90 | public static byte[] UseFontA => SelectCharacterFont(Font.A); 91 | public static byte[] UseFontB => SelectCharacterFont(Font.B); 92 | 93 | public static byte[] AlignToLeft => SelectJustification(Justification.Left); 94 | public static byte[] AlignToRight => SelectJustification(Justification.Right); 95 | public static byte[] AlignToCenter => SelectJustification(Justification.Center); 96 | 97 | public static byte[] CharSizeDoubleHeight => SelectCharSize(CharSizeWidth.Normal, CharSizeHeight.Double); 98 | public static byte[] CharSizeDoubleWidth => SelectCharSize(CharSizeWidth.Double, CharSizeHeight.Normal); 99 | public static byte[] CharSizeDoubleHeightAndWidth => SelectCharSize(CharSizeWidth.Double, CharSizeHeight.Double); 100 | public static byte[] CharSizeReset => SelectCharSize(CharSizeWidth.Normal, CharSizeHeight.Normal); 101 | 102 | public static byte[] HRIAboveBarcode => SelectHRIPosition(HRIPosition.AboveBarcode); 103 | public static byte[] HRIBelowBarcode => SelectHRIPosition(HRIPosition.BelowBarcode); 104 | public static byte[] HRINotPrinted => SelectHRIPosition(HRIPosition.NotPrinted); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /ESCPOSTest/UnitTest.cs: -------------------------------------------------------------------------------- 1 | using ESCPOS; 2 | using ESCPOS.Utils; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using System; 5 | using System.IO; 6 | using System.Text; 7 | using static ESCPOS.Commands; 8 | using static ESCPOSTest.StringDiffHelper; 9 | 10 | namespace ESCPOSTest 11 | { 12 | [TestClass] 13 | public class UnitTest 14 | { 15 | private const string TEST_FILE = "test.txt"; 16 | private const string TEXT_DATA = "Data test with some special characters: $ñáãç*/&#@\"'^{}"; 17 | 18 | [TestMethod] 19 | public void Add_TwoByteArrays_FirstNull() 20 | { 21 | byte[] array1 = null; 22 | array1 = array1.Add(LF); 23 | ShouldEqualWithDiff(array1.ToUTF8(), LF.ToUTF8()); 24 | } 25 | 26 | [TestMethod] 27 | public void Add_TwoByteArrays_SecondNull() 28 | { 29 | byte[] array2 = null; 30 | array2 = LF.Add(array2); 31 | ShouldEqualWithDiff(array2.ToUTF8(), LF.ToUTF8()); 32 | } 33 | 34 | [TestMethod] 35 | public void Add_TwoByteArrays() 36 | { 37 | byte[] array = LF.Add(HT); 38 | ShouldEqualWithDiff(array.ToUTF8(), $"{LF.ToUTF8()}{HT.ToUTF8()}"); 39 | } 40 | 41 | [TestMethod] 42 | public void Add_ThreeStrings() 43 | { 44 | byte[] array = null; 45 | array = array.Add(TEXT_DATA, TEXT_DATA, TEXT_DATA); 46 | ShouldEqualWithDiff(array.ToUTF8(), $"{TEXT_DATA}{TEXT_DATA}{TEXT_DATA}"); 47 | } 48 | 49 | [TestMethod] 50 | public void Add_FourByteArrays() 51 | { 52 | byte[] array = LF.Add(HT, CR, FF); 53 | ShouldEqualWithDiff(array.ToUTF8(), $"{LF.ToUTF8()}{HT.ToUTF8()}{CR.ToUTF8()}{FF.ToUTF8()}"); 54 | } 55 | 56 | [TestMethod] 57 | public void Add_ThreeByteArraysAndTwoStrings() 58 | { 59 | byte[] array = LF.Add(TEXT_DATA, CR, TEXT_DATA, FF); 60 | ShouldEqualWithDiff(array.ToUTF8(), $"{LF.ToUTF8()}{TEXT_DATA}{CR.ToUTF8()}{TEXT_DATA}{FF.ToUTF8()}"); 61 | } 62 | 63 | [TestMethod] 64 | public void Add_IgnoringNonStringNonByteArray() 65 | { 66 | byte[] array = LF.Add(TEXT_DATA, 1, CR, DateTime.Now, 10.5, 785m, new UnitTest(), FF); 67 | ShouldEqualWithDiff(array.ToUTF8(), $"{LF.ToUTF8()}{TEXT_DATA}{CR.ToUTF8()}{FF.ToUTF8()}"); 68 | } 69 | 70 | [TestMethod] 71 | public void QRCode_NoParameter() 72 | { 73 | QRCode(TEXT_DATA).Print(TEST_FILE); 74 | ShouldEqualWithDiff("\u001D(k\u0004\01A1\0\u001D(k\u0003\01C\u0004\u001D(k\u0003\01E0\u001D(k:\01P0Data test with some special characters: $ñáãç*/&#@\"'^{}\u001D(k\u0003\01Q0", File.ReadAllText(TEST_FILE)); 75 | } 76 | 77 | [TestMethod] 78 | public void QRCode_FullParameters() 79 | { 80 | QRCode(TEXT_DATA, QRCodeModel.Model2, QRCodeCorrection.Percent30, QRCodeSize.Large).Print(TEST_FILE); 81 | ShouldEqualWithDiff("\u001D(k\u0004\01A2\0\u001D(k\u0003\01C\u0005\u001D(k\u0003\01E3\u001D(k:\01P0Data test with some special characters: $ñáãç*/&#@\"'^{}\u001D(k\u0003\01Q0", File.ReadAllText(TEST_FILE)); 82 | } 83 | 84 | [TestMethod] 85 | public void Barcode_EAN8() 86 | { 87 | Barcode(BarCodeType.EAN8, "90311017", 52).Print(TEST_FILE); 88 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001Dh4\u001dw\u0003\u001DkD\u000890311017"); 89 | } 90 | 91 | [TestMethod] 92 | public void Barcode_EAN13() 93 | { 94 | Barcode(BarCodeType.EAN13, "9780201379624", 52).Print(TEST_FILE); 95 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001Dh4\u001dw\u0003\u001DkC\r9780201379624"); 96 | } 97 | 98 | [TestMethod] 99 | public void Barcode_CODE128() 100 | { 101 | Barcode(BarCodeType.CODE128, "ABC1234", 52).Print(TEST_FILE); 102 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001Dh4\u001dw\u0003\u001DkI\u0009{BABC1234"); 103 | } 104 | 105 | [TestMethod] 106 | public void Barcode_UPC_A() 107 | { 108 | Barcode(BarCodeType.UPC_A, "72527273070", 52).Print(TEST_FILE); 109 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001Dh4\u001dw\u0003\u001DkA\u000B72527273070"); 110 | } 111 | 112 | [TestMethod] 113 | public void DoubleHeight() 114 | { 115 | SelectCharSizeHeight(CharSizeHeight.Double).Add(TEXT_DATA).Print(TEST_FILE); 116 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001D!\u0001Data test with some special characters: $ñáãç*/&#@\"'^{}"); 117 | } 118 | 119 | [TestMethod] 120 | public void DoubleWidthAndHeight() 121 | { 122 | SelectCharSize(CharSizeWidth.Double, CharSizeHeight.Double).Add(TEXT_DATA).Print(TEST_FILE); 123 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001D!\u0011Data test with some special characters: $ñáãç*/&#@\"'^{}"); 124 | } 125 | 126 | [TestMethod] 127 | public void DoubleWidth() 128 | { 129 | SelectCharSizeWidth(CharSizeWidth.Double).Add(TEXT_DATA).Print(TEST_FILE); 130 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001D!\u0010Data test with some special characters: $ñáãç*/&#@\"'^{}"); 131 | } 132 | 133 | [TestMethod] 134 | public void AlignCenter() 135 | { 136 | SelectJustification(Justification.Center).Add(TEXT_DATA).Print(TEST_FILE); 137 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001Ba\u0001Data test with some special characters: $ñáãç*/&#@\"'^{}"); 138 | } 139 | 140 | [TestMethod] 141 | public void AlignRight() 142 | { 143 | SelectJustification(Justification.Right).Add(TEXT_DATA).Print(TEST_FILE); 144 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001Ba\u0002Data test with some special characters: $ñáãç*/&#@\"'^{}"); 145 | } 146 | 147 | [TestMethod] 148 | public void AlignLeft() 149 | { 150 | SelectJustification(Justification.Left).Add(TEXT_DATA).Print(TEST_FILE); 151 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "\u001Ba\0Data test with some special characters: $ñáãç*/&#@\"'^{}"); 152 | } 153 | 154 | [TestMethod] 155 | public void PrinterSameLineLeftAndRightAlignedText() 156 | { 157 | var printer = new Printer(); 158 | printer.Columns = 5; 159 | byte[] result; 160 | 161 | //Pr 99 162 | result = printer.SameLineLeftAndRightAlignedText("Product", "99"); 163 | ShouldEqualWithDiff(result.ToUTF8(), "Pr 99\u001Ba\u0000"); 164 | 165 | //P 999 166 | result = printer.SameLineLeftAndRightAlignedText("Product", "999"); 167 | ShouldEqualWithDiff(result.ToUTF8(), "P 999\u001Ba\u0000"); 168 | 169 | //Pro 99 170 | printer.Columns = 6; 171 | result = printer.SameLineLeftAndRightAlignedText("Product", "99"); 172 | ShouldEqualWithDiff(result.ToUTF8(), "Pro 99\u001Ba\u0000"); 173 | 174 | // 99 175 | printer.Columns = 3; 176 | result = printer.SameLineLeftAndRightAlignedText("Product", "99"); 177 | ShouldEqualWithDiff(result.ToUTF8(), "\u001Ba\u000299\u001Ba\u0000"); 178 | 179 | // 99 180 | printer.Columns = 2; 181 | result = printer.SameLineLeftAndRightAlignedText("Product", "99"); 182 | ShouldEqualWithDiff(result.ToUTF8(), "\u001Ba\u000299\u001Ba\u0000"); 183 | 184 | //Product 99 185 | printer.Columns = 10; 186 | result = printer.SameLineLeftAndRightAlignedText("Product", "99"); 187 | ShouldEqualWithDiff(result.ToUTF8(), "Product 99\u001Ba\u0000"); 188 | 189 | //Product 99 190 | printer.Columns = 15; 191 | result = printer.SameLineLeftAndRightAlignedText("Product", "99"); 192 | ShouldEqualWithDiff(result.ToUTF8(), "Product 99\u001Ba\u0000"); 193 | 194 | 195 | printer = new Printer { Encoding = Encoding.UTF8, Columns = 32, Address = TEST_FILE }; 196 | printer.AddToCache( 197 | printer.HorizontalDoubleLine, 198 | LF, 199 | printer.SameLineLeftAndRightAlignedText("Product Name", "Price"), 200 | LF, 201 | printer.HorizontalLine, 202 | LF, 203 | printer.SameLineLeftAndRightAlignedText("Sample Product", "$10.99"), 204 | LF, 205 | printer.SameLineLeftAndRightAlignedText("Sample Product with a very long description", "$0.01"), 206 | LF, 207 | AlignToRight, 208 | "----------", 209 | LF, 210 | CharSizeDoubleHeight, 211 | "$11.00", 212 | CharSizeReset, 213 | LF, 214 | "----------", 215 | AlignToLeft, 216 | LF, 217 | printer.HorizontalDoubleLine 218 | ); 219 | printer.Print(); 220 | ShouldEqualWithDiff(File.ReadAllText(TEST_FILE), "================================\nProduct Name Price\u001ba\u0000\n--------------------------------\nSample Product $10.99\u001ba\u0000\nSample Product with a very $0.01\u001ba\u0000\n\u001ba\u0002----------\n\u001d!\u0001$11.00\u001d!\u0000\n----------\u001ba\u0000\n================================"); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /ESCPOS/ESCPOSCommands/Commands.cs: -------------------------------------------------------------------------------- 1 | using ESCPOS.Utils; 2 | using System; 3 | using System.IO; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | 7 | namespace ESCPOS 8 | { 9 | public static partial class Commands 10 | { 11 | /// 12 | /// ESC ! n 13 | /// 14 | public static byte[] SelectPrintMode(PrintMode printMode) 15 | => new byte[] { 0x1B, 0x21, (byte)printMode }; 16 | 17 | /// 18 | /// ESC - n 19 | /// 20 | public static byte[] SelectUnderlineMode(UnderlineMode underlineMode) 21 | => new byte[] { 0x1B, 0x2D, (byte)underlineMode }; 22 | 23 | /// 24 | /// ESC 2 / ESC 3 n 25 | /// 26 | public static byte[] SelectLineSpacing(LineSpacing lineSpacing) 27 | => lineSpacing == LineSpacing.Default ? new byte[] { 0x1B, (byte)lineSpacing } : new byte[] { 0x1B, 0x33, (byte)lineSpacing }; 28 | 29 | /// 30 | /// ESC G n 31 | /// 32 | public static byte[] DoubleStrike(OnOff @switch) 33 | => new byte[] { 0x1B, 0x47, (byte)@switch }; 34 | 35 | /// 36 | /// ESC M n 37 | /// 38 | public static byte[] SelectCharacterFont(Font font) 39 | => new byte[] { 0x1B, 0x4D, (byte)font }; 40 | 41 | /// 42 | /// ESC R n 43 | /// 44 | public static byte[] SelectInternationalCharacterSet(CharSet charSet) 45 | => new byte[] { 0x1B, 0x52, (byte)charSet }; 46 | 47 | /// 48 | /// ESC T n 49 | /// 50 | public static byte[] SelectPrintDirection(Direction direction) 51 | => new byte[] { 0x1B, 0x54, (byte)direction }; 52 | 53 | /// 54 | /// ESC V n 55 | /// 56 | public static byte[] Turn90Degrees(ClockwiseDirection clockwiseDirection) 57 | => new byte[] { 0x1B, 0x56, (byte)clockwiseDirection }; 58 | 59 | /// 60 | /// ESC a n 61 | /// 62 | public static byte[] SelectJustification(Justification justification) 63 | => new byte[] { 0x1B, 0x61, (byte)justification }; 64 | 65 | /// 66 | /// ESC t n 67 | /// 68 | public static byte[] SelectCodeTable(CodeTable codeTable) 69 | => new byte[] { 0x1B, 0x74, (byte)codeTable }; 70 | 71 | /// 72 | /// ESC { n 73 | /// 74 | public static byte[] UpsideDown(OnOff @switch) 75 | => new byte[] { 0x1B, 0x7B, (byte)@switch }; 76 | 77 | /// 78 | /// GS ! n 79 | /// 80 | public static byte[] SelectCharSize(CharSizeWidth charSizeWidth, CharSizeHeight charSizeHeight) 81 | { 82 | var charSize = (byte)charSizeWidth | (byte)charSizeHeight; 83 | return new byte[] { 0x1D, 0x21, (byte)charSize }; 84 | } 85 | 86 | /// 87 | /// GS ! n 88 | /// 89 | public static byte[] SelectCharSizeHeight(CharSizeHeight charSize) 90 | => new byte[] { 0x1D, 0x21, (byte)charSize }; 91 | 92 | /// 93 | /// GS ! n 94 | /// 95 | public static byte[] SelectCharSizeWidth(CharSizeWidth charSize) 96 | => new byte[] { 0x1D, 0x21, (byte)charSize }; 97 | 98 | /// 99 | /// GS H n 100 | /// 101 | public static byte[] SelectHRIPosition(HRIPosition hriPosition) 102 | => new byte[] { 0x1D, 0x21, (byte)hriPosition }; 103 | 104 | /// 105 | /// GS k m n 106 | /// 107 | /// is . 108 | [Obsolete(nameof(PrintBarCode) + " is deprecated, please use " + nameof(Barcode) + " method instead.", true)] 109 | public static byte[] PrintBarCode(BarCodeType barcodeType, string barcode, int heightInDots = 162, BarcodeWidth barcodeWidth = BarcodeWidth.Normal) 110 | => Barcode(barcodeType, barcode, heightInDots, barcodeWidth); 111 | 112 | /// 113 | /// GS k m n 114 | /// 115 | /// is . 116 | public static byte[] Barcode(BarCodeType barcodeType, string barcode, int heightInDots = 162, BarcodeWidth barcodeWidth = BarcodeWidth.Normal, Encoding encoding = null) 117 | { 118 | var height = new byte[] { 0x1D, 0x68, (byte)heightInDots }; 119 | var width = new byte[] { 0x1D, 0x77, (byte)barcodeWidth }; 120 | var length = barcode.Length; 121 | var bar = (encoding ?? Encoding.UTF8).GetBytes(barcode); 122 | if (barcodeType == BarCodeType.CODE128) 123 | { 124 | length += 2; 125 | bar = new byte[] { 0x7B, 0x42 }.Add(bar); //Subset CODE B is selected for CODE128 bars 126 | } 127 | var settings = new byte[] { 0x1D, 0x6B, (byte)barcodeType, (byte)length }; 128 | 129 | return height.Add(width, settings, bar); 130 | } 131 | 132 | public static byte[] ToBarcode(this string barcode, BarCodeType barCodeType, int heightInDots = 162, BarcodeWidth barcodeWidth = BarcodeWidth.Normal, Encoding encoding = null) 133 | => Barcode(barCodeType, barcode, heightInDots, barcodeWidth, encoding); 134 | 135 | /// 136 | /// GS ( k pL pH cn fn n1 n2 137 | /// 138 | /// is . 139 | [Obsolete(nameof(PrintQRCode) + " is deprecated, please use " + nameof(QRCode) + " method instead.", true)] 140 | public static byte[] PrintQRCode(string content, QRCodeModel qrCodeModel = QRCodeModel.Model1, QRCodeCorrection qrCodeCorrection = QRCodeCorrection.Percent7, QRCodeSize qrCodeSize = QRCodeSize.Normal) 141 | => QRCode(content, qrCodeModel, qrCodeCorrection, qrCodeSize); 142 | 143 | 144 | /// 145 | /// GS ( k pL pH cn fn n1 n2 146 | /// 147 | /// is . 148 | public static byte[] QRCode(string content, QRCodeModel qrCodeModel = QRCodeModel.Model1, QRCodeCorrection qrCodeCorrection = QRCodeCorrection.Percent7, QRCodeSize qrCodeSize = QRCodeSize.Normal, Encoding encoding = null) 149 | { 150 | var model = new byte[] { 0x1D, 0x28, 0x6B, 0x04, 0x00, 0x31, 0x41, (byte)qrCodeModel, 0x00 }; 151 | var size = new byte[] { 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x43, (byte)qrCodeSize }; 152 | var errorCorrection = new byte[] { 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x45, (byte)qrCodeCorrection }; 153 | int num = content.Length + 3; 154 | int pL = num % 256; 155 | int pH = num / 256; 156 | var storeData = new byte[] { 0x1D, 0x28, 0x6B, (byte)pL, (byte)pH, 0x31, 0x50, 0x30 }; 157 | var data = (encoding ?? Encoding.UTF8).GetBytes(content); 158 | var print = new byte[] { 0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30 }; 159 | return model.Add(size, errorCorrection, storeData, data, print); 160 | } 161 | 162 | public static byte[] ToQRCode(this string content, QRCodeModel qrCodeModel = QRCodeModel.Model1, QRCodeCorrection qrCodeCorrection = QRCodeCorrection.Percent7, QRCodeSize qrCodeSize = QRCodeSize.Normal, Encoding encoding = null) 163 | => QRCode(content, qrCodeModel, qrCodeCorrection, qrCodeSize, encoding); 164 | 165 | 166 | /// is empty, or in an unexpected format. 167 | /// is . 168 | public static void Print(this byte[] data, string printerAddress) 169 | { 170 | if (printerAddress == null) 171 | throw new ArgumentNullException(nameof(printerAddress)); 172 | 173 | if (printerAddress.Length == 0) 174 | throw new ArgumentException("Printer address can't be empty", nameof(printerAddress)); 175 | 176 | string[] splittedAddress = printerAddress.Split(':'); 177 | 178 | //if printerAddress is a directly connected printer without sharing address, it will be accepted as "{host}:{port}" format 179 | if (splittedAddress.Length == 2) 180 | { 181 | string host = splittedAddress[0]; 182 | string port = splittedAddress[1]; 183 | if (!int.TryParse(port, out var portNumber)) 184 | throw new ArgumentException($"Print address format should be {{host}}:{{port}}, but instead it is {host}:{portNumber}"); 185 | 186 | using (Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) 187 | { 188 | clientSocket.NoDelay = true; 189 | clientSocket.Connect(splittedAddress[0], portNumber); 190 | clientSocket.Send(data); 191 | } 192 | return; 193 | } 194 | 195 | string tempFile = "esc_pos.temp"; 196 | if (File.Exists(tempFile)) 197 | { 198 | try 199 | { 200 | File.Delete(tempFile); 201 | } 202 | catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) 203 | { 204 | tempFile = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{tempFile}"; 205 | } 206 | } 207 | 208 | File.WriteAllBytes(tempFile, data); 209 | File.Copy(tempFile, printerAddress, true); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![SourceLink](https://img.shields.io/badge/SourceLink-enabled-brightgreen)](https://github.com/dotnet/sourcelink) 2 | [![Build Status](https://dev.azure.com/igorocampos/PersonalProjects/_apis/build/status/igorocampos.ESCPOS?branchName=master)](https://dev.azure.com/igorocampos/PersonalProjects/_build?definitionId=1&_a=summary) 3 | [![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/igorocampos/ESCPOS)](#) 4 | [![NuGet](https://img.shields.io/nuget/v/ESCPOS)](http://www.nuget.org/packages/ESCPOS) 5 | [![GitHub](https://img.shields.io/badge/license-MIT-green)](ESCPOS/LICENSE) 6 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Figorocampos%2FESCPOS.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Figorocampos%2FESCPOS?ref=badge_shield) 7 | 8 | # ESCPOS 9 | A ESC/POS Printer Commands Helper. 10 | 11 | ![](https://github.com/igorocampos/ESCPOS/blob/master/ESC_POS.png) 12 | 13 | # Installing via NuGet Package 14 | 15 | The NuGet Package can be found [here](https://www.nuget.org/packages/ESCPOS/) and you can install it with: 16 | 17 | ```powershell 18 | PM> Install-Package ESCPOS 19 | ``` 20 | 21 | # Usage 22 | All command methods will return a **byte array** that you should concatenate with the bytes of your data, and then send it all to your printer using the `Print` extension method, which will send a byte array to the informed printer address. It can be something like `COM3`, `LPT1`, `\\127.0.0.1\printer`, `192.168.0.100:9100`, etc. or even a path to a text file like `./print.txt`. 23 | 24 | There is also 2 extension methods, `Add` and `ToBytes`, located in the namespace `ESCPOS.Utils`. 25 | The first one can be used in byte arrays, so you can concatenate 2 or more byte arrays just like this: 26 | ```cs 27 | byte[] result = array1.Add(array2, array3, ..., arrayN); 28 | ``` 29 | In addition there's an overload to the mentioned `Add` method that will accept string parameters instead of byte arrays. It appends all strings into one new string and then converts it to a byte array. 30 | And yet another overload to accept the mix of byte arrays and strings parameters, but since it's accepting an object type parameter, this will ignore any parameter that is not `string` or `byte[]` (e.g. `int`). 31 | 32 | With `ToBytes` method you can convert a UTF-8 string to a byte array: 33 | ```cs 34 | byte[] result = "Some string".ToBytes(); 35 | ``` 36 | 37 | Alternatively you can choose whatever Encoding you wish to use for that: 38 | ```cs 39 | byte[] result = "汉字".ToBytes(Encoding.GetEncoding("GBK")); 40 | ``` 41 | *Just make sure your Printer has a corresponding CodePage for that Encoding! 42 | 43 | ## Printer Class 44 | As an alternative, if you'd like to instantiate a Printer class that will handle all about bytes, encoding, and even have printer specific features like an horizontal line or print text aligned to the left AND to the right in the same line, which is only possible by knowing the number of columns your printer has, you can use the `Printer` class and its methods. 45 | 46 | ```cs 47 | //Instantiate printer with its details for encoding, column count and address 48 | var printer = new Printer { Encoding = Encoding.UTF8, Columns = 32, Address = "COM4" }; 49 | 50 | //Add all bytes to the Cache 51 | printer.AddToCache( 52 | printer.HorizontalLine, 53 | LF, 54 | printer.SameLineLeftAndRightAlignedText("Sample Product", "$10.99"), 55 | LF, 56 | printer.HorizontalLine 57 | ); 58 | 59 | //Send everything that is currently in the cache to the printer 60 | printer.Print(); 61 | ``` 62 | 63 | ## Examples 64 | 65 | All examples will assume the using statements below: 66 | ```cs 67 | using static ESCPOS.Commands; 68 | using ESCPOS; 69 | using ESCPOS.Utils; 70 | ``` 71 | 72 | ### QRCode 73 | ```cs 74 | byte[] qrCodeCommand = QRCode("Some data"); 75 | qrCodeCommand.Print("COM2"); 76 | ``` 77 | 78 | Or using the Extension Method 79 | 80 | ```cs 81 | string data = "Some data"; 82 | data.ToQRCode().Print("COM2"); 83 | ``` 84 | 85 | 86 | ### Barcode 87 | ```cs 88 | byte[] barCodeCommand = Barcode(BarCodeType.EAN13, "9780201379624"); 89 | barCodeCommand.Print("192.168.0.100:9100"); 90 | ``` 91 | 92 | Or using the Extension Method 93 | 94 | ```cs 95 | string code = "9780201379624"; 96 | code.ToBarcode(BarCodeType.EAN13).Print("192.168.0.100:9100"); 97 | ``` 98 | 99 | 100 | ### Formatted Text 101 | ```cs 102 | byte[] cmd = AlignToCenter.Add(CharSizeDoubleHeight, "Fancy Title"); 103 | cmd.Print(@"\\127.0.0.1\printer"); 104 | ``` 105 | ### Example using Printer class 106 | ```cs 107 | var printer = new Printer { Encoding = Encoding.UTF8, Columns = 32, Address = "192.168.0.100:9100" }; 108 | printer.AddToCache( 109 | printer.HorizontalDoubleLine, 110 | LF, 111 | printer.SameLineLeftAndRightAlignedText("Product Name", "Price"), 112 | LF, 113 | printer.HorizontalLine, 114 | LF, 115 | printer.SameLineLeftAndRightAlignedText("Sample Product", "$10.99"), 116 | LF, 117 | printer.SameLineLeftAndRightAlignedText("Sample Product with a very long description", "$0.01"), 118 | LF, 119 | AlignToRight, 120 | "----------", 121 | LF, 122 | CharSizeDoubleHeight, 123 | "$11.00", 124 | CharSizeReset, 125 | LF, 126 | "----------", 127 | AlignToLeft, 128 | LF, 129 | printer.HorizontalDoubleLine 130 | ); 131 | printer.Print(); 132 | ``` 133 | 134 | ### Full CFe SAT Receipt without using Printer class 135 | This example will assume that the variable `cfe` is a deserialized object from the [CFe](https://portal.fazenda.sp.gov.br/servicos/sat) XML, and will print the receipt using its fields. 136 | Also this example will print a 32 columns receipt, which is ideal for 56mm paper roll. 137 | ```cs 138 | var line = "--------------------------------"; 139 | 140 | byte[] array = LF; 141 | array = array.Add(CharSizeDoubleHeight, AlignToCenter); 142 | 143 | if (cfe.infCFe.emit.xFant != null) 144 | array.Add(cfe.infCFe.emit.xFant); 145 | 146 | array.Add(LF, CharSizeReset, cfe.infCFe.emit.xNome, 147 | LF, $"{cfe.infCFe.emit.enderEmit.xLgr},{cfe.infCFe.emit.enderEmit.nro} {cfe.infCFe.emit.enderEmit.xBairro} - {cfe.infCFe.emit.enderEmit.xMun} {cfe.infCFe.emit.enderEmit.CEP}", 148 | LF, $"CNPJ: {cfe.infCFe.emit.CNPJ}", 149 | LF, $"IE: {cfe.infCFe.emit.IE}", 150 | LF, line, CharSizeDoubleHeight, $"Extrato No. {cfe.infCFe.ide.nCFe}", 151 | LF, "CUPOM FISCAL ELETRONICO - SAT", CharSizeReset, 152 | LF, LF); 153 | 154 | if (!string.IsNullOrEmpty(cfe.infCFe.dest?.Item)) 155 | array.Add(line, "CPF/CNPJ do Consumidor:", cfe.infCFe.dest.Item, LF); 156 | 157 | array.Add(line, 158 | "#|COD|DESC|QTD|UN|VL UNIT R$|(VL TRIB R$)*|VL ITEM R$", LF, 159 | line, 160 | AlignLeft); 161 | 162 | int i = 1; 163 | foreach (var det in cfe.infCFe.det) 164 | { 165 | string prod = $"{det.prod.cProd} {det.prod.xProd} {det.prod.qCom:0.0##} {det.prod.uCom} X {det.prod.vUnCom:0.00#} {((det.imposto?.vItem12741 ?? 0) == 0 ? "" : $"({det.imposto.vItem12741:f2})*")}"; 166 | array.Add($" {i++:D3} "); 167 | while (prod.Length > 20) 168 | { 169 | var wrap = prod.Length >= 20 ? prod.Substring(0, 20) : prod; 170 | array.Add(wrap), LF, " "); 171 | prod = prod.Substring(20); 172 | } 173 | array.Add(prod.PadRight(20), det.prod.vProd.ToString("f2").PadLeft(6), LF); 174 | } 175 | 176 | array.Add(LF); 177 | 178 | if (cfe.infCFe.total.ICMSTot.vDesc > 0) 179 | array.Add($" Desconto R${cfe.infCFe.total.ICMSTot.vDesc.ToString("f2").PadLeft(19)}", LF); 180 | 181 | if (cfe.infCFe.total.ICMSTot.vOutro > 0) 182 | array.Add($" Acrescimo R${cfe.infCFe.total.ICMSTot.vOutro.ToString("f2").PadLeft(18)}", LF); 183 | 184 | array.Add(CharSizeDoubleHeight, $" TOTAL R${cfe.infCFe.total.vCFe.ToString("f2").PadLeft(22)}", LF, 185 | CharSizeReset, LF); 186 | 187 | foreach (var mp in cfe.infCFe.pgto.MP) 188 | { 189 | string description; 190 | switch (Convert.ToInt32(mp.cMP ?? "1")) 191 | { 192 | case 2: 193 | description = "Cheque"; 194 | break; 195 | case 3: 196 | description = "Cartao de Credito"; 197 | break; 198 | case 4: 199 | description = "Cartao de Debito"; 200 | break; 201 | case 5: 202 | description = "Credito na Loja"; 203 | break; 204 | case 10: 205 | description = "Vale Alimentacao"; 206 | break; 207 | case 11: 208 | description = "Vale Refeicao"; 209 | break; 210 | case 12: 211 | description = "Vale Presente"; 212 | break; 213 | case 13: 214 | description = "Vale Combustivel"; 215 | break; 216 | case 14: 217 | description = "Duplicata Mercantil"; 218 | break; 219 | case 90: 220 | description = "Sem Pagamento"; 221 | break; 222 | default: 223 | description = "Dinheiro"; 224 | break; 225 | } 226 | 227 | array.Add($" {description.PadRight(18)}{mp.vMP.ToString("f2").PadLeft(12)}", LF); 228 | } 229 | 230 | String accessKey = cfe.infCFe.Id.Substring(3, 44); 231 | array.Add($" Troco{cfe.infCFe.pgto.vTroco.ToString("f2").PadLeft(25)}", LF); 232 | 233 | foreach (var obs in cfe.infCFe.infAdic.obsFisco) 234 | array.Add($" {obs.xCampo}-{obs.xTexto}", LF); 235 | 236 | array.Add(line, "OBSERVACOES DO CONTRIBUINTE", LF); 237 | foreach (var item in cfe.infCFe.infAdic.infCpl.Split(';')) 238 | array.Add(item, LF); 239 | 240 | array.Add(LF, line, CharSizeDoubleHeight, AlignTOCenter, $"SAT No. {cfe.infCFe.ide.nserieSAT}", 241 | LF, CharSizeReset, DateTime.ParseExact($"{cfe.infCFe.ide.dEmi} {cfe.infCFe.ide.hEmi}", "yyyyMMdd HHmmss", System.Globalization.CultureInfo.InvariantCulture).ToString("dd/MM/yyyy HH:mm:ss"), 242 | LF, LF, CharSizeDoubleHeight, accessKey, 243 | LF, LF, accessKey.Substring(0, 22).ToBarcode(BarCodeType.CODE128, 30), accessKey.Substring(22).ToBarcode(BarCodeType.CODE128, 30), 244 | LF, LF, 245 | $"{accessKey}|{cfe.infCFe.ide.dEmi}{cfe.infCFe.ide.hEmi}|{cfe.infCFe.total.vCFe}|{cfe.infCFe.dest?.Item ?? ""}|{cfe.infCFe.ide.assinaturaQRCODE}".ToQRCode(), 246 | CharSizeReset, 247 | LF, line, LF, LF, LF); 248 | 249 | array.Print(@"\\127.0.0.1\printer"); 250 | ``` 251 | 252 | # Considerations 253 | When printing CODE128 barcodes, it will use automatically subset B, which supports numbers, upper and lower case letters and some additional characters. 254 | 255 | You can see the changelog [here](CHANGELOG.md). 256 | 257 | 258 | ## License 259 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Figorocampos%2FESCPOS.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Figorocampos%2FESCPOS?ref=badge_large) 260 | 261 | --------------------------------------------------------------------------------