【问题标题】:Printing without print service preview没有打印服务预览的打印
【发布时间】:2018-06-04 20:54:59
【问题描述】:

我想在我的应用程序中按下按钮并将 pdf 文档发送到打印机直接打印(不显示来自打印框架 Android 4.4 的系统 android 预览)。我该怎么做? 我试图通过 Socket 连接到打印机。没问题,没有例外,但我的打印机没有响应,也没有打印任何内容。

也许我需要在手机上为混凝土打印机设置驱动程序?但是怎么做,我在哪里可以得到这样的驱动程序?

已编辑

【问题讨论】:

    标签: java android printing android-print-framework


    【解决方案1】:

    我编写了一个类来帮助将 PDF 文件直接打印到网络打印机,并提供其 IP。它应该可以在大多数打印机上工作,只要它们支持 PJL 命令。

    public class PrintService {
    
        private static PrintListener printListener;
    
        public enum PaperSize {
            A4,
            A5
        }
    
        public static void printPDFFile(final String printerIP, final int printerPort,
                                        final File file, final String filename, final PaperSize paperSize, final int copies) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    Socket socket = null;
                    DataOutputStream out = null;
                    FileInputStream inputStream = null;
                    try {
                        socket = new Socket(printerIP, printerPort);
                        out = new DataOutputStream(socket.getOutputStream());
                        DataInputStream input = new DataInputStream(socket.getInputStream());
                        inputStream = new FileInputStream(file);
                        byte[] buffer = new byte[3000];
    
                        final char ESC = 0x1b;
                        final String UEL = ESC + "%-12345X";
                        final String ESC_SEQ = ESC + "%-12345\r\n";
    
                        out.writeBytes(UEL);
                        out.writeBytes("@PJL \r\n");
                        out.writeBytes("@PJL JOB NAME = '" + filename + "' \r\n");
                        out.writeBytes("@PJL SET PAPER=" + paperSize.name());
                        out.writeBytes("@PJL SET COPIES=" + copies);
                        out.writeBytes("@PJL ENTER LANGUAGE = PDF\r\n");
                        while (inputStream.read(buffer) != -1)
                            out.write(buffer);
                        out.writeBytes(ESC_SEQ);
                        out.writeBytes("@PJL \r\n");
                        out.writeBytes("@PJL RESET \r\n");
                        out.writeBytes("@PJL EOJ NAME = '" + filename + "'");
                        out.writeBytes(UEL);
    
                        out.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                        if (printListener != null)
                            printListener.networkError();
                    } finally {
                        try {
                            if (inputStream != null)
                                inputStream.close();
                            if (out != null)
                                out.close();
                            if (socket != null)
                                socket.close();
                            if (printListener != null)
                                printListener.printCompleted();
                        } catch (IOException e) {
                            e.printStackTrace();
                            if (printListener != null)
                                printListener.networkError();
                        }
                    }
                }
            });
            t.start();
        }
    
        public static void setPrintListener(PrintListener list) {
            printListener = list;
        }
    
        public interface PrintListener {
            void printCompleted();
    
            void networkError();
        }
    }
    

    【讨论】:

    • 假设它接受 PJL 并且知道如何在打印机中本地打印 PDF。我从事打印机固件工作已有十多年了,但在当时,第二个固件将是一个巨大的假设。
    • 谢谢,这似乎是一个很好的解决方案。但我发现错误:java.net.SocketException: sendto failed: ECONNRESET (Connection reset by peer) 我更新了我的问题,添加了错误图片
    • 您要发送到哪个端口?如果我没记错的话,PJL 通常在 9100 上工作。
    • @Cristian 我试过 631 端口。我以为所有打印机都连接到 631 端口。好的,我试试9100。
    • 谢谢@Cristian,您的解决方案有效,但使用其他打印机。我的第一台打印机不支持 PDF 和 PostScript
    【解决方案2】:

    @Cristian 用 AsyncTask 实现回答。通过此实现,您可以针对 PrintServiceListener 回调执行 UI 操作。

    public class CustomPrinterService extends AsyncTask<Void, Void, Boolean> {
    
    public enum PaperSize {
        A4,
        A5
    }
    
    private static final String TAG = "CustomPrinterService";
    
    private PrintServiceListener mPrintServiceListener;
    
    private String mPrinterIP;
    private String mFilename;
    
    private int mPrinterPort;
    private int mNumberOfCopies;
    
    private File mFile;
    private PaperSize mPaperSize;
    
    public CustomPrinterService(final String printerIP, final int printerPort, final File file,
                                final String filename, final PaperSize paperSize, final int copies) {
        mPrinterIP = printerIP;
        mPrinterPort = printerPort;
        mFile = file;
        mFilename = filename;
        mPaperSize = paperSize;
        mNumberOfCopies = copies;
    }
    
    @Override
    protected Boolean doInBackground(Void... voids) {
        Boolean result = null;
        Socket socket = null;
        DataOutputStream out = null;
        FileInputStream inputStream = null;
        try {
            socket = new Socket(mPrinterIP, mPrinterPort);
            out = new DataOutputStream(socket.getOutputStream());
            DataInputStream input = new DataInputStream(socket.getInputStream());
            inputStream = new FileInputStream(mFile);
            byte[] buffer = new byte[3000];
    
            final char ESC = 0x1b;
            final String UEL = ESC + "%-12345X";
            final String ESC_SEQ = ESC + "%-12345\r\n";
    
            out.writeBytes(UEL);
            out.writeBytes("@PJL \r\n");
            out.writeBytes("@PJL JOB NAME = '" + mFilename + "' \r\n");
            out.writeBytes("@PJL SET PAPER=" + mPaperSize.name());
            out.writeBytes("@PJL SET COPIES=" + mNumberOfCopies);
            out.writeBytes("@PJL ENTER LANGUAGE = PDF\r\n");
            while (inputStream.read(buffer) != -1)
                out.write(buffer);
            out.writeBytes(ESC_SEQ);
            out.writeBytes("@PJL \r\n");
            out.writeBytes("@PJL RESET \r\n");
            out.writeBytes("@PJL EOJ NAME = '" + mFilename + "'");
            out.writeBytes(UEL);
    
            out.flush();
        } catch (Exception exception) {
            Log.d(TAG, exception.toString());
            result = false;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (out != null) {
                    out.close();
                }
                if (socket != null) {
                    socket.close();
                }
                if (result == null) {
                    result = true;
                }
            } catch (Exception exception) {
                Log.d(TAG, exception.toString());
                result = false;
            }
        }
        return result;
    }
    
    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);
        if (result) {
            if (mPrintServiceListener != null) {
                mPrintServiceListener.onPrintCompleted();
            }
        } else {
            if (mPrintServiceListener != null) {
                mPrintServiceListener.onNetworkError();
            }
        }
    }
    
    public void setPrintServiceListener(PrintServiceListener listener) {
        mPrintServiceListener = listener;
    }
    
    public interface PrintServiceListener {
    
        void onPrintCompleted();
    
        void onNetworkError();
    }
    

    }

    【讨论】:

      【解决方案3】:

      除了 Gokula 的回答:

      1. 也可以在设备上不保存文件的情况下执行此操作。就我而言,我使用字节数组或 base 64 来打印图像。

      2. kotlin 中的代码 =)

      打印机控制器调用

      override fun write(content: String, address: String?) {
          address?.let {
              val policy: StrictMode.ThreadPolicy = StrictMode.ThreadPolicy.Builder().permitAll().build()
              StrictMode.setThreadPolicy(policy)
              Base64.decode("Base64 image content goes here", Base64.DEFAULT).printByDeviceIp(address)
          }
      }
      

      打印机扩展

      fun ByteArray.printByDeviceIp(address: String) {
          try {
              val socket = Socket(address, PRINTER_DEFAULT_PORT)
              val output = DataOutputStream(socket.getOutputStream())
              val buffer = ByteArray(PRINTER_BUFFER_SIZE)
              val inputStream = ByteArrayInputStream(this)
      
              output.writeBytes(UEL)
              output.writeBytes(PRINT_META_JOB_START)
              output.writeBytes(PRINT_META_JOB_NAME)
              output.writeBytes(PRINT_META_JOB_PAPER_TYPE)
              output.writeBytes(PRINT_META_JOB_COPIES)
              output.writeBytes(PRINT_META_JOB_LANGUAGE)
      
              while (inputStream.read(buffer) != -1)
                  output.write(buffer)
      
              output.writeBytes(ESC_SEQ)
              output.writeBytes(UEL)
      
              output.flush()
      
              inputStream.close()
              output.close()
              socket.close()
          } catch (e: Exception) {
              when(e) {
              is SocketException -> Log.e(this.javaClass.name, "Network failure: ${e.message}")
              is SocketTimeoutException -> Log.e(this.javaClass.name, "Timeout: ${e.message}")
              is IOException -> Log.e(this.javaClass.name, "Buffer failure: ${e.message}")
              else -> Log.e(this.javaClass.name, "General failure: ${e.message}")
          }
      }
      

      打印机作业常量

      private const val PRINT_META_JOB_LABEL = "@PJL"
      private const val PRINT_META_BREAK = "\r\n"
      
      private const val ESCAPE_KEY = 0x1b.toChar()
      const val UEL = "$ESCAPE_KEY%-12345X"
      const val ESC_SEQ = "$ESCAPE_KEY%-12345 $PRINT_META_BREAK"
      
      const val PRINT_META_JOB_START = "$PRINT_META_JOB_LABEL $PRINT_META_BREAK"
      const val PRINT_META_JOB_NAME = "$PRINT_META_JOB_LABEL JOB NAME = 'INBOUND_FINISH' $PRINT_META_BREAK"
      const val PRINT_META_JOB_PAPER_TYPE = "$PRINT_META_JOB_LABEL SET PAPER = A4"
      const val PRINT_META_JOB_COPIES = "$PRINT_META_JOB_LABEL SET COPIES = 1"
      const val PRINT_META_JOB_LANGUAGE = "$PRINT_META_JOB_LABEL ENTER LANGUAGE = PDF $PRINT_META_BREAK"
      
      const val PRINTER_DEFAULT_PORT: Int = 9100
      const val PRINTER_BUFFER_SIZE: Int = 3000
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多