通过C#在串口打印机上打印位图图像

11

我是通过串口直接将收据打印到POS打印机上,方法如下:

        SerialPort port = new SerialPort("com6", 9100, Parity.None, 8, StopBits.One);
        port.Open();
        port.Write("Some Text");
        port.Close();
我的问题是如何使用上述方法打印位图图像?任何帮助将不胜感激。
我还没有决定使用Microsoft POS for.net,因为它速度较慢,并且需要初始化打印机,而客户不喜欢等待。
谢谢。

你需要阅读打印机制造商的编程手册,以便知道要发送给打印机的字节。这将会很慢。 - Hans Passant
5
这里有一个非常类似的问题:https://dev59.com/b-o6XIcBkEYKwwoYORwV。因此,我提名重新开放这个问题,因为可以证明它并不是过于局限性的。 - Andrew Alcock
2
希望我能投票支持重新开放这个问题...“过于局限”听起来像是一个玩笑:o - Dominique
1
我认为这也是一个有效的问题。 - AaA
3个回答

35

这将使您从位图中获得一个字符串,您可以将其发送到打印机:

    public string GetLogo()
    {
        string logo = "";
        if (!File.Exists(@"C:\bitmap.bmp"))
            return null;
         BitmapData data = GetBitmapData(@"C:\bitmap.bmp");
         BitArray dots = data.Dots;
         byte[] width = BitConverter.GetBytes(data.Width);

         int offset = 0;
         MemoryStream stream = new MemoryStream();
         BinaryWriter bw = new BinaryWriter(stream);

         bw.Write((char)0x1B);
         bw.Write('@');

         bw.Write((char)0x1B);
         bw.Write('3');
         bw.Write((byte)24);

         while (offset < data.Height)
         {
             bw.Write((char)0x1B);
             bw.Write('*');         // bit-image mode
             bw.Write((byte)33);    // 24-dot double-density
             bw.Write(width[0]);  // width low byte
             bw.Write(width[1]);  // width high byte

             for (int x = 0; x < data.Width; ++x)
             {
                 for (int k = 0; k < 3; ++k)
                 {
                     byte slice = 0;
                     for (int b = 0; b < 8; ++b)
                     {
                         int y = (((offset / 8) + k) * 8) + b;
                         // Calculate the location of the pixel we want in the bit array.
                         // It'll be at (y * width) + x.
                         int i = (y * data.Width) + x;

                         // If the image is shorter than 24 dots, pad with zero.
                         bool v = false;
                         if (i < dots.Length)
                         {
                             v = dots[i];
                         }
                         slice |= (byte)((v ? 1 : 0) << (7 - b));
                     }

                     bw.Write(slice);
                 }
             }
             offset += 24;
             bw.Write((char)0x0A);
         }
         // Restore the line spacing to the default of 30 dots.
         bw.Write((char)0x1B);
         bw.Write('3');
         bw.Write((byte)30);

         bw.Flush();
         byte[] bytes = stream.ToArray();
         return logo + Encoding.Default.GetString(bytes);
    }

    public BitmapData GetBitmapData(string bmpFileName)
    {
        using (var bitmap = (Bitmap)Bitmap.FromFile(bmpFileName))
        {
            var threshold = 127;
            var index = 0;
            double multiplier = 570; // this depends on your printer model. for Beiyang you should use 1000
            double scale = (double)(multiplier/(double)bitmap.Width);
            int xheight = (int)(bitmap.Height * scale);
            int xwidth = (int)(bitmap.Width * scale);
            var dimensions = xwidth * xheight;
            var dots = new BitArray(dimensions);

            for (var y = 0; y < xheight; y++)
            {
                for (var x = 0; x < xwidth; x++)
                {
                    var _x = (int)(x / scale);
                    var _y = (int)(y / scale);
                    var color = bitmap.GetPixel(_x, _y);
                    var luminance = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);
                    dots[index] = (luminance < threshold);
                    index++;
                }
            }

            return new BitmapData()
            {
                Dots = dots,
                Height = (int)(bitmap.Height*scale),
                Width = (int)(bitmap.Width*scale)
            };
        }
    }

    public class BitmapData
    {
        public BitArray Dots
        {
            get;
            set;
        }

        public int Height
        {
            get;
            set;
        }

        public int Width
        {
            get;
            set;
        }
    }

这个真的有效... - Aman Sura
它必须是位图吗?还是我也可以使用它来打印JPEG和PNG? - Smith
这个解决方案真是救命稻草!谢谢! - tzes
我该如何确定我需要哪个乘数?在我的打印机编程指南中没有出现“乘数”这个词。 - blue
1
@blue,我不记得这是什么意思或者为什么我会这样称呼它。已经很久了 :) 如果我理解正确的话 - 这是打印机输出像素宽度。 - avivklas
显示剩余5条评论

0

可能现在还没有用处,但我认为要直接打印到打印机,您必须找到编程手册并发送命令转义符。

这种类型的打印机有自己的一套命令等等,可以格式化、旋转文本、打印条形码、上传和打印图像等等。

除非您知道语言并正确使用它,否则打印机的行为可能是不可预测的。


是的,你说得对。但是我观察到通过串口连接的大多数POS设备的ESC | Pos命令几乎相同。我已经在爱普生,Bixolon和E-Pos打印机上进行了测试,这些命令都是相同的。 - Nasif
一些命令是相同的,一些则不同。大约有20个不同的“打开抽屉”命令。 - Jasen

-10
我找到了另一种方法,请大家帮忙传播!
步骤1: 下载NV Image软件(nv_image_tool_v3.1.6),并通过Comport将图像设置到打印机 请按照图片中的步骤进行操作 enter image description here 步骤2:将代码添加到您喜欢的按钮中:
 private void button1_Click(object sender, EventArgs e)
{
 SerialPort port = new SerialPort("com6", 9100, Parity.None, 8, StopBits.One);
 port.Open();
 ASCIIEncoding ascii = new ASCIIEncoding();
 port.Write(ascii.GetString(new byte[] { 28, 112, 1, 0})); //Printing the Above uploaded Logo
 port.WriteLine("Your Text");
 port.Close();
}

是的,这个工具使工作变得容易,因为我们不需要编写代码将标志注册到打印机中。 - Nasif
1
是的,但这是针对编程的QA,而不是第三方软件的教程网站。 - Simon Whitehead
1
谢谢你的评论,伙计。如果你知道比这更好的方法,请在这里发布。无论如何,这是大多数打印机制造商提供的标准软件,可以使工作更加轻松。特别是Bixolon。 - Nasif
这并不是一个很有用的答案,如果你在Linux或MacOS上怎么办?很多打印机制造商不支持其他不是Windows的操作系统,如果你在Android或iOS设备上怎么办呢? - Necronet
如果您不使用Windows,仍然可以在Windows PC上设置图像,然后永久从Linux或任何其他操作系统打印它。 - kuma DK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接