You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter如何将PDF打印至热敏打印机?解决类型转换异常

解决Flutter中使用print_bluetooth_thermal打印PDF收据到热敏打印机的问题

问题分析

  1. 直接打印PDF字节的错误本质:
    你遇到的ClassCastException: byte[] cannot be cast to java.util.List是插件Android端的类型转换问题,但核心矛盾是热敏打印机不支持直接解析PDF文件,必须将PDF内容转换成打印机可识别的格式(如单色适配图片、ESC/POS指令)才能正常打印。

  2. 转图片打印失败的常见原因:

    • 图片尺寸未匹配打印机固定宽度(热敏打印机通常为58mm/80mm,对应特定像素宽度)
    • 未将图片转为单色(热敏打印机仅支持黑白打印,彩色/灰度图片会出现模糊或无法识别)
    • 直接写入PNG字节未经过打印机适配的指令封装,导致设备无法解析

可行解决方案

方案1:优化PDF转单色适配图片打印

修改现有转图片逻辑,调整尺寸、转为单色,并生成打印机可识别的ESC/POS指令:

import 'dart:ui' as ui;
import 'package:printing/printing.dart';
import 'package:image/image.dart' as img; // 需要添加image包依赖

Future<void> printPdfAsImage(File pdfFile) async {
  if (!pdfFile.existsSync()) {
    print('文件不存在');
    return;
  }

  bool conexionStatus = await PrintBluetoothThermal.connectionStatus;
  if (!conexionStatus) {
    print('打印机未连接');
    return;
  }

  final pdfBytes = pdfFile.readAsBytesSync();
  // 80mm宽打印机用203DPI对应640像素,58mm宽对应384像素,按需调整
  final pages = Printing.raster(pdfBytes, dpi: 203, width: 640);

  await for (final page in pages) {
    final image = await page.toImage();
    final byteData = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
    if (byteData == null) continue;

    // 将UI图片转为image库格式
    final img.Image rawImage = img.Image.fromBytes(
      width: image.width,
      height: image.height,
      bytes: byteData.buffer.asUint8List(),
      format: img.Format.rgba,
    );

    // 转为黑白单色图片
    final img.Image monoImage = img.grayscale(rawImage);
    final img.Image thresholdImage = img.threshold(monoImage, threshold: 128);

    // 生成ESC/POS图片打印指令并发送
    final List<int> escPosCommand = _generateEscPosImage(thresholdImage);
    final result = await PrintBluetoothThermal.writeBytes(escPosCommand);
    print("打印结果: $result");

    // 打印后添加换行
    await PrintBluetoothThermal.writeBytes('\n\n'.codeUnits);
  }

  pdfFile.delete();
}

// 生成适配热敏打印机的ESC/POS图片指令
List<int> _generateEscPosImage(img.Image image) {
  List<int> command = [];
  // 初始化打印机
  command.addAll([0x1B, 0x40]);
  // 设置单密度图片打印模式
  command.addAll([0x1B, 0x2A, 0x01]);
  // 图片宽度(字节数,向上取整)
  int widthBytes = (image.width + 7) ~/ 8;
  command.addAll([widthBytes & 0xFF, (widthBytes >> 8) & 0xFF]);
  // 图片高度
  command.addAll([image.height & 0xFF, (image.height >> 8) & 0xFF]);
  // 逐行写入图片像素数据(黑白反转适配热敏打印)
  for (int y = 0; y < image.height; y++) {
    for (int x = 0; x < widthBytes; x++) {
      byte pixelByte = 0;
      for (int bit = 0; bit < 8; bit++) {
        int pixelX = x * 8 + bit;
        if (pixelX < image.width) {
          int pixel = image.getPixel(pixelX, y);
          // 黑色像素设为1,白色设为0
          if (img.getRed(pixel) < 128) {
            pixelByte |= (1 << (7 - bit));
          }
        }
      }
      command.add(pixelByte);
    }
  }
  return command;
}

方案2:直接生成ESC/POS收据(推荐)

如果你的PDF是结构简单的收据类文档,跳过PDF直接生成ESC/POS指令是更高效的方式,避免转图片的性能损耗:

Future<void> printReceiptDirectly() async {
  bool conexionStatus = await PrintBluetoothThermal.connectionStatus;
  if (!conexionStatus) {
    print('打印机未连接');
    return;
  }

  List<int> receipt = [];
  // 初始化打印机
  receipt.addAll([0x1B, 0x40]);
  // 设置字体加粗
  receipt.addAll([0x1B, 0x45, 0x01]);
  // 添加标题
  receipt.addAll('订单收据\n'.codeUnits);
  receipt.addAll([0x1B, 0x45, 0x00]); // 取消加粗
  // 订单详情
  receipt.addAll('订单号: 20240520001\n'.codeUnits);
  receipt.addAll('日期: 2024-05-20\n'.codeUnits);
  receipt.addAll('------------------------\n'.codeUnits);
  receipt.addAll('商品: 奶茶 x1\n'.codeUnits);
  receipt.addAll('金额: 15.00元\n'.codeUnits);
  receipt.addAll('------------------------\n'.codeUnits);
  receipt.addAll('总计: 15.00元\n'.codeUnits);
  // 切纸指令
  receipt.addAll([0x1D, 0x56, 0x00]);

  final result = await PrintBluetoothThermal.writeBytes(receipt);
  print("打印结果: $result");
}

关键注意事项

  • 添加依赖:使用image库需在pubspec.yaml中添加image: ^4.0.17(版本号以最新为准)
  • 确认打印机的实际宽度和DPI,调整代码中的对应参数
  • 优先选择直接生成ESC/POS指令的方式,打印速度更快、效果更稳定

内容的提问来源于stack exchange,提问作者Boby

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:29:56