C# ESC/POS 打印越南语

4
我有一个应用程序,它使用ESC/POS打印机打印收据。需要支持多种语言。目前,我已经测试了中文(繁体和简体)和泰语,它们都可以正常工作。
然而,当我尝试打印越南语时,一些字符被替换成“?”。
这是我的代码:
public static readonly string ESC = "\u001B";
...
...
...
Encoding enc = Encoding.GetEncoding(1258); //vietnamese code page
string content = "Cơm chiên với các loại gia vị truyền thống làm cho lưỡi của bạn";
string toPrint = ESC + "t" + char.ConvertFromUtf32(94) + "\n" + Encoding.GetEncoding("Latin1").GetString(enc.GetBytes(str));  //code page 94 is for vietnamese (WPC1258). It is get from printer

在打印出来的内容中,一些字符被替换成了“?”(请参见附图)。有什么想法吗?

Print Out

更新

根据Panagiotis Kanavos的评论,我尝试了以下方法:

Encoding enc = Encoding.GetEncoding(1258); //vietnamese code page
string content = "Cơm chiên với các loại gia vị truyền thống làm cho lưỡi của bạn";
string newStr = Encoding.GetEncoding("Latin1").GetString(enc.GetBytes(content));
string origStr = enc.GetString(Encoding.GetEncoding("Latin1").GetBytes(newStr));

原始字符串包含?。我确认在中文和泰语中,origStr将等于content。但对于越南语来说不是这样的。有什么想法吗?
更新2
我决定使用Panagiotis Kanavos的代码,即bytesToPrint = enc.GetBytes("\x1Bt\x5E\n" + content);,但它给出了与实际字符不同的?结果。
这是我将内容(字符串或字节)发送到打印机的方式。
[DllImport("Winspool.drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);

[DllImport("Winspool.drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(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 = "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 = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

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

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

public static bool SendBytesToPrinter(string printerName, IntPtr pBytes, int dwCount, string docName = null, string dataType = "RAW")
{
    DOCINFOA di = new DOCINFOA();
    di.pDocName = string.IsNullOrWhiteSpace(docName) ? string.Empty : docName;
    di.pDataType = string.IsNullOrWhiteSpace(dataType) ? "RAW" : dataType;

    IntPtr hPrinter = new IntPtr(0); int dwError = 0, dwWritten = 0; bool bSuccess = false;
    if (OpenPrinter(printerName.Normalize(), out hPrinter, IntPtr.Zero))
    {
        if (StartDocPrinter(hPrinter, 1, di))
        {
            if (StartPagePrinter(hPrinter))
            {
                bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                EndPagePrinter(hPrinter);
            }
            EndDocPrinter(hPrinter);
        }
        ClosePrinter(hPrinter);
    }

    if (bSuccess == false)
        dwError = Marshal.GetLastWin32Error();
    return bSuccess;
}

public static bool SendBytesToPrinter(string printerName, byte[] bytes, string docName)
{
    int dwCount = bytes.Length;
    IntPtr ptrBytes = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(byte)) * bytes.Length);
    try
    {
        Marshal.Copy(bytes, 0, ptrBytes, bytes.Length);
        SendBytesToPrinter(printerName, ptrBytes, dwCount, docName);
    }
    finally { Marshal.FreeCoTaskMem(ptrBytes); }
    return true;
}

public static bool SendStringToPrinter(string printerName, string str, string docName)
{
    int dwCount = str.Length;
    IntPtr ptrBytes = Marshal.StringToCoTaskMemAnsi(str);
    try { SendBytesToPrinter(printerName, ptrBytes, dwCount, docName); }
    finally { Marshal.FreeCoTaskMem(ptrBytes); }
    return true;
}

1
.NET的编写器类(如StreamWriter)默认使用UTF8。如果您使用编写器写入POS,则应该可以正常工作,无需进行代码页转换。如果您复制字节数组,则必须找出配置的代码页。最好的选择是配置设备使用UTF8,并仅使用Encoding.UTF8.GetBytes()来获取正确的字节。在任何其他情况下,您需要创建一个编码对象,例如使用Encoding.GetEncoding(1258),并仅使用其GetBytes()方法来获取正确的字节。但是,您将失去无法转换为该代码页的任何字符。 - Panagiotis Kanavos
@PanagiotisKanavos,不幸的是,在ESC/P打印时,它就是这样工作的。在发送到ESC/P之前,您必须将其转换回拉丁文。然后它将与所选打印机的代码页匹配(打印机初始化时选择的代码页)。目前,该代码适用于中文/日语/泰语。 - Sam
@Sam,你仍然没有解释那种方式是什么。你是直接写入串行端口吗?打印到文本打印机?使用数据流?你所说的打印机型号是什么?你的代码在哪里?不,没有打印机要求在发送数据之前销毁数据。这就是将不同代码页转换为Latin 1所做的。中文不会工作,一旦切换代码页,它就会被破坏。泰语也行不通。如果你想打印越南语,改变打印机的代码页。 - Panagiotis Kanavos
@Sam,你用的是什么打印机,什么型号?你查看了手册,可能有控制码吗?顺便说一下,char.ConvertFromUtf32(94)只是^字符。该代码将控制码前置到字符串中,具体来说是"\x1Bt^\n"\x1B\u001b是相同的字符,即ESC。我怀疑打印机希望控制序列以ESC开头并带有换行符,这意味着你向打印机发送了^t代码。 - Panagiotis Kanavos
@PanagiotisKanavos 我正在使用 ISSYZONE POS ITPP012。94 是越南语的打印机代码页。每个打印机都有自己独特的代码页。例如,对于 EPSON,越南语的 CodePage 是 52 (https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=32)。*ESC t* 用于告诉打印机要使用哪个代码页。在上面的例子中,ESC t 94 告诉打印机使用代码页 94。 - Sam
显示剩余10条评论
2个回答

0

-2

这是你需要的全部内容,以便使用C#正确地打印出越南字符。

将你的代码编辑为以下内容:

public static readonly string ESC = "\u001B";
...
...
...
//Encoding enc = Encoding.GetEncoding(1258); //vietnamese code page
string content = "Cơm chiên với các loại gia vị truyền thống làm cho lưỡi của bạn";
string toPrint = ESC + "t" + char.ConvertFromUtf32(94) + "\n"; 
// First you need to convert the vietnamese string to utf-8 bytes.
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(content); 
// Convert utf-8 bytes to a string.
toPrint += System.Text.Encoding.UTF8.GetString(utf8Bytes);

尝试以上代码,如果您仍然遇到奇怪的字符,请告诉我...


你好 Indago,它不起作用:( 事实上,它让情况变得更糟。现在,其他语言也不能工作了。中文和泰语都无法工作。您必须使用适当的编码方式。在越南语中,编码方式是1258。您是否设法在您的端口解决问题? - Sam
C#(以及Windows)使用UTF16,这就是为什么“content”可以首次编译的原因。它是Unicode。没有理由使用“UTF8.GetBytes”。但是,如果POS不支持UTF16或UTF8,则必须进行配置。 - Panagiotis Kanavos
顺便说一句,这是毫无疑问的。SO是一个.NET应用程序,因此它可以显示任何字符,包括这个问题中的越南文本,而无需特殊处理。 - Panagiotis Kanavos
@PanagiotisKanavos .NET 可以正常显示字符...只是在打印时出现问题...它可以在中文、日文和泰文上正常工作...只是在越南文上无法正常工作。 - Sam

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