certflow.services.printer.lq635kii_printer 源代码

# src/certflow/services/printer/lq635kii_printer.py
"""LQ-635KII 打印机底层驱动

基于 ESC/P-K 命令集,包含手册 P101 所有命令及补充功能

定位策略:
- X 轴:绝对定位 ESC $(有效)
- Y 轴:相对移动 ESC J(有效),绝对定位 ESC ( V 无效
"""

from __future__ import annotations

try:
    import win32print

    _HAS_WIN32 = True
except ImportError:
    _HAS_WIN32 = False
    win32print = None  # type: ignore[assignment]

from .escp_commands import (
    Align,
    Font,
    LQ635KIICommand,
    PaperSize,
    PrinterDPI,
    PrintQuality,
)


[文档] class LQ635KIIPrinter: """LQ-635KII 打印机控制类 专门为 EPSON LQ-635KII 24针打印机设计 使用 ESC/P-K 命令集,包含手册全部命令 定位策略: - X: 绝对定位(ESC $),自动应用 x_offset - Y: 相对移动(ESC J),自动应用 y_offset 使用示例: # 1. 基础打印(推荐使用上下文管理器) with LQ635KIIPrinter("EPSON LQ-635KII") as printer: printer.init() printer.set_line_spacing_1_6() printer.println("产品名称:高温高压阀门") printer.println("产品型号:J41H-64") printer.println("公称通径:DN150") printer.println("公称压力:PN64") printer.form_feed() # 2. 精确定位打印(预印卡纸)- 使用推荐方法 with LQ635KIIPrinter() as printer: printer.init() printer.set_quality(PrintQuality.LETTER) # 方法1:分别设置 X 和 Y(推荐) printer.set_absolute_x_mm(25) # X=25mm(绝对定位,自动应用 x_offset) printer.set_absolute_y_mm(30) # Y=30mm(相对移动实现,自动应用 y_offset) printer.print_text("DN150") printer.set_absolute_x_mm(25) printer.set_absolute_y_mm(38) printer.print_text("PN64") printer.form_feed() # 2.1 或者使用合并方法 with LQ635KIIPrinter() as printer: printer.init() printer.set_quality(PrintQuality.LETTER) printer.set_absolute_xy_mm(25, 30) # 同时设置 X,Y printer.print_text("DN150") printer.set_absolute_xy_mm(25, 38) printer.print_text("PN64") printer.form_feed() # 3. 图形模式打印(任意字体/字号) with LQ635KIIPrinter() as printer: printer.init() # 需要配合 PrinterService 使用 pass # 4. 手动控制(不使用上下文管理器) printer = LQ635KIIPrinter("EPSON LQ-635KII") printer.open() printer.init() printer.set_bold(True) printer.println("重要提示") printer.set_bold(False) printer.flush() # 立即发送 printer.close() """ def __init__( self, printer_name: str | None = None, calibration_x_offset_mm: float = 13, y_offset_mm: float = 0, ) -> None: """ 初始化打印机 Args: printer_name: 打印机名称,None 则使用默认打印机 calibration_x_offset_mm: 打印机物理校准 X 偏移补偿 (mm),默认 13。 用于补偿打印机硬件装纸/打印头起点的物理偏差,对**所有**文字统一 叠加,正值向右。与英制字段偏移(TemplateFieldFormatter 的 imperial_x_offset_mm / PrinterService.x_offsets)语义不同、相互独立。 y_offset_mm: Y 方向偏移补偿 (mm),默认 0(与 PrinterService._load_y_offset 回退一致) """ self.printer_name = printer_name if printer_name is None: self.printer_name = win32print.GetDefaultPrinter() if _HAS_WIN32 else "" self._hprinter = None self._buffer = bytearray() self._is_open = False self._printer_cal_x_offset = calibration_x_offset_mm self._y_offset = y_offset_mm self._current_y_mm = 0.0 # 维护当前 Y 位置(用于相对移动) self._line_spacing_mm = 4.23 # 默认 1/6 英寸 self._last_job_id: int | None = None # 最近一次 _flush 提交的 OS 作业 ID # 原始数据监控 self._raw_monitor = False self._raw_monitor_file: str | None = None # ============================================================ # 基础方法 # ============================================================
[文档] def open(self) -> LQ635KIIPrinter: """打开打印机连接 如果已打开则跳过,支持链式调用。 Returns: LQ635KIIPrinter: 返回自身实例,支持链式调用 """ if not self._is_open: self._hprinter = win32print.OpenPrinter(self.printer_name) self._is_open = True return self
[文档] def close(self) -> None: """关闭打印机连接 先刷新缓冲区将剩余数据发送到打印机,再关闭打印机句柄。 """ if self._hprinter and self._is_open: try: self._flush() win32print.ClosePrinter(self._hprinter) except Exception: pass finally: self._hprinter = None self._is_open = False
[文档] def __enter__(self) -> LQ635KIIPrinter: """上下文管理器入口,自动打开打印机 Returns: LQ635KIIPrinter: 自身实例 """ self.open() return self
[文档] def __exit__(self, *args: object) -> None: """上下文管理器出口,自动关闭打印机""" self.close()
def _append(self, data: bytes) -> None: """追加原始字节数据到打印缓冲区 Args: data: 要追加的原始字节数据 """ self._buffer.extend(data) if self._raw_monitor: self._log_raw(data) def _flush(self, job_name: str = "CertFlow") -> int | None: """将缓冲区数据作为一次打印作业发送到打印机 使用 RAW 模式发送,适用于 ESC/P-K 命令数据。 发送完毕后清空缓冲区。 捕获并打印作业提交后由 spooler 分配的 OS 作业 ID(``StartDocPrinter`` 的返回值),用于后续 OS 级取消 / 状态查询(见 BLUEPRINT §2.3 偏差 5 作业 ID 捕获链路)。无 ``win32print``(headless)时返回 ``None``。 Args: job_name: 打印作业名称,会写入监控日志便于区分不同任务 Returns: int | None: OS 打印作业 ID;未真正提交(无句柄 / 空缓冲)时返回 None """ if not self._buffer: return None if not self._is_open: self.open() all_data = bytes(self._buffer) if self._raw_monitor: self._log_flush(all_data, job_name) job_id: int | None = None if self._hprinter: job_id = win32print.StartDocPrinter(self._hprinter, 1, (job_name, None, "RAW")) win32print.StartPagePrinter(self._hprinter) win32print.WritePrinter(self._hprinter, all_data) win32print.EndPagePrinter(self._hprinter) win32print.EndDocPrinter(self._hprinter) self._buffer.clear() self._last_job_id = job_id return job_id
[文档] def flush(self, job_name: str = "CertFlow") -> int | None: """强制将缓冲区数据发送到打印机 Args: job_name: 打印作业名称 Returns: int | None: 本次提交的 OS 打印作业 ID(见 :meth:`_flush`) """ return self._flush(job_name)
# ============================================================ # 原始数据监控 # ============================================================
[文档] def enable_raw_monitoring( self, output_dir: str = "raw_logs", filter_cmds: list[bytes] | None = None ) -> None: """启用原始数据监控 启用后,每次 _append 和 _flush 都会记录原始字节数据: - 控制台:实时显示 [RAW] 十六进制日志 - 文件:raw_full_YYYYMMDD_HHMMSS.prn 保存完整数据流 Args: output_dir: 输出目录路径 filter_cmds: 可选,仅记录包含这些字节序列的数据块。 例如 [b'\\x1b$', b'\\x1b(V'] 只记录定位命令相关数据。 为 None 时记录所有数据。 """ from datetime import datetime from pathlib import Path self._raw_monitor = True self._raw_filter_cmds = filter_cmds or [] log_dir = Path(output_dir) log_dir.mkdir(parents=True, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") self._raw_monitor_file = str(log_dir / f"raw_full_{timestamp}.prn") print("[RAW MONITOR] 已启用") print(f" 输出目录: {log_dir.resolve()}") print(f" 完整数据: {self._raw_monitor_file}") if self._raw_filter_cmds: print( f" 过滤模式: 仅记录包含 {[c.hex().upper() for c in self._raw_filter_cmds]} 的数据" )
[文档] def disable_raw_monitoring(self) -> None: """禁用原始数据监控""" self._raw_monitor = False self._raw_monitor_file = None self._raw_filter_cmds = [] print("[RAW MONITOR] 已禁用")
def _log_raw(self, data: bytes) -> None: """记录单次 _append 的原始数据""" # 过滤模式:仅记录匹配过滤条件的数据块 if self._raw_filter_cmds and not any(cmd in data for cmd in self._raw_filter_cmds): return hex_str = data.hex(" ").upper() ascii_str = "".join(chr(b) if 32 <= b < 127 else "." for b in data) print(f"[RAW +{len(data):3d}] {hex_str:<48s} |{ascii_str}|") def _log_flush(self, all_data: bytes, job_name: str = "CertFlow") -> None: """记录 _flush 时的完整数据""" print(f"\n[FLUSH] 发送 {len(all_data)} 字节到打印机") if self._raw_monitor_file: # 检查文件大小,超过50MB自动轮转 import os if os.path.exists(self._raw_monitor_file): file_size = os.path.getsize(self._raw_monitor_file) if file_size > 50 * 1024 * 1024: base = self._raw_monitor_file.replace(".prn", "") new_file = f"{base}_2.prn" print(f"[RAW MONITOR] 日志轮转: {file_size / 1024 / 1024:.1f}MB -> {new_file}") self._raw_monitor_file = new_file # 写入作业分隔标识 with open(self._raw_monitor_file, "ab") as f: f.write(b"\n" + f"=== JOB: {job_name} ===\n".encode() + b"\n") f.write(all_data) print(f"[FLUSH] 数据已追加到: {self._raw_monitor_file}") # ============================================================ # 基本控制 # ============================================================
[文档] def carriage_return(self) -> None: """回车 (CR) 将打印位置移动到当前行的最左侧。 """ self._append(LQ635KIICommand.CR)
[文档] def backspace(self) -> None: """退格 (BS) 打印位置向左移动一个字符位。 """ self._append(LQ635KIICommand.BS)
[文档] def cancel_line(self) -> None: """取消行缓冲区 (CAN) 清除当前行缓冲区中的所有数据。 """ self._append(LQ635KIICommand.CAN)
[文档] def delete_char(self) -> None: """删除字符 (DEL) 删除上一个字符。 """ self._append(LQ635KIICommand.DEL)
[文档] def online(self) -> None: """联机 (DC1) 将打印机设置为联机状态,准备接收数据。 """ self._append(LQ635KIICommand.DC1)
[文档] def offline(self) -> None: """脱机 (DC3) 将打印机设置为脱机状态,停止接收数据。 """ self._append(LQ635KIICommand.DC3)
# ============================================================ # 初始化与基本控制 # ============================================================
[文档] def init(self) -> None: """初始化打印机 (ESC @) 清除缓冲区,恢复打印机到开机默认状态。 会先发送缓冲区中未完成的数据,避免丢失。 """ self._flush() # 发送未完成的数据 self._buffer = bytearray(LQ635KIICommand.INIT) # 重置为 INIT 命令
[文档] def home(self) -> None: """打印头归位 (ESC <) 将打印头移动到左侧起始位置。 """ self._append(LQ635KIICommand.HOME) self._current_y_mm = 0.0
[文档] def beep(self) -> None: """蜂鸣器鸣叫 (BEL) 发送蜂鸣命令,打印机发出提示音。 """ self._append(LQ635KIICommand.BEL)
[文档] def feed(self, lines: int = 1) -> None: """走纸 N 行 (LF) Args: lines: 行数,取值范围 1-255,超出自动截断 """ if lines > 0: self._append(LQ635KIICommand.LF * min(lines, 255)) self._current_y_mm += lines * self._line_spacing_mm # 1/6 英寸 ≈ 4.23mm
[文档] def form_feed(self) -> None: """换页 (FF) 将纸张推进到下一页顶部位置。 """ self._append(LQ635KIICommand.FF) self._current_y_mm = 0.0
# ============================================================ # 纸张格式 # ============================================================
[文档] def set_paper_size(self, paper: PaperSize) -> None: """设置纸张尺寸 Args: paper: 纸张尺寸对象 """ if self._hprinter: try: devmode = win32print.GetPrinter(self._hprinter, 2)["pDevMode"] devmode.PaperSize = 256 devmode.PaperWidth = int(paper.width_mm * 10) devmode.PaperLength = int(paper.height_mm * 10) win32print.SetPrinter(self._hprinter, 2, devmode, 0) except Exception: pass
[文档] def set_left_margin(self, col: int) -> None: """设置左边界 (ESC l n) Args: col: 列数 (在10cpi下) """ self._append(LQ635KIICommand.LEFT_MARGIN + bytes([col & 0xFF]))
[文档] def set_right_margin(self, col: int) -> None: """设置右边界 (ESC Q n) Args: col: 列数 (在10cpi下) """ self._append(LQ635KIICommand.RIGHT_MARGIN + bytes([col & 0xFF]))
[文档] def set_line_spacing(self, n: int, unit: str = "180") -> None: """设置行间距 Args: n: 间距值 (1-255) unit: 单位 ("60", "180", "360") """ n_val = min(max(n, 0), 255) if unit == "60": self._append(LQ635KIICommand.LINE_SPACING_N_60 + bytes([n_val])) self._line_spacing_mm = n_val * 25.4 / 60 elif unit == "360": self._append(LQ635KIICommand.LINE_SPACING_N_360 + bytes([n_val])) self._line_spacing_mm = n_val * 25.4 / 360 else: # "180" self._append(LQ635KIICommand.LINE_SPACING_N_180 + bytes([n_val])) self._line_spacing_mm = n_val * 25.4 / 180
[文档] def set_line_spacing_1_6(self) -> None: """设置行间距为 1/6 英寸(打印机默认值) 适用于常规文本打印。 """ self._append(LQ635KIICommand.LINE_SPACING_1_6) self._line_spacing_mm = 25.4 / 6 # ≈ 4.233mm
[文档] def set_line_spacing_1_8(self) -> None: """设置行间距为 1/8 英寸 比默认间距更紧凑。 """ self._append(LQ635KIICommand.LINE_SPACING_1_8) self._line_spacing_mm = 25.4 / 8 # ≈ 3.175mm
[文档] def set_skip_perf(self, lines: int) -> None: """设置页缝跳过 (ESC N n) Args: lines: 每页底部跳过的行数 """ self._append(LQ635KIICommand.PAGE_SKIP_PERF + bytes([lines & 0xFF]))
[文档] def cancel_skip_perf(self) -> None: """取消页缝跳过 (ESC O) 取消之前设置的页缝跳过功能,恢复完整页面打印。 """ self._append(LQ635KIICommand.PAGE_SKIP_OFF)
[文档] def set_page_length_lines(self, lines: int) -> None: """设置页长(按行) (ESC C n) Args: lines: 每页行数,取值范围 1-127 """ self._append(LQ635KIICommand.PAGE_LENGTH_LINES + bytes([min(lines, 127) & 0xFF]))
[文档] def set_page_length_inch(self, inches: int) -> None: """设置页长(按英寸) (ESC C NUL n) Args: inches: 每页长度,单位英寸,取值范围 1-22 """ self._append(LQ635KIICommand.PAGE_LENGTH_INCH + bytes([min(inches, 22) & 0xFF]))
# ============================================================ # 对齐 # ============================================================
[文档] def set_align(self, align: Align) -> None: """设置对齐方式 (ESC a n) Args: align: 对齐方式枚举值 (LEFT/CENTER/RIGHT/JUSTIFY) """ self._append(LQ635KIICommand.ALIGN_LEFT + bytes([align.value]))
[文档] def left_align(self) -> None: """设置左对齐""" self.set_align(Align.LEFT)
[文档] def center(self) -> None: """设置居中对齐""" self.set_align(Align.CENTER)
[文档] def right_align(self) -> None: """设置右对齐""" self.set_align(Align.RIGHT)
[文档] def justify(self) -> None: """设置两端对齐""" self.set_align(Align.JUSTIFY)
# ============================================================ # 打印品质与字体 # ============================================================
[文档] def set_quality(self, quality: PrintQuality) -> None: """设置打印品质 (ESC x n) Args: quality: 打印品质,DRAFT(草体/快速) 或 LETTER(文书体/高质量) """ if quality == PrintQuality.DRAFT: self._append(LQ635KIICommand.DRAFT_QUALITY) else: self._append(LQ635KIICommand.LETTER_QUALITY)
[文档] def set_draft(self) -> None: """设置为草体模式(快速打印)""" self.set_quality(PrintQuality.DRAFT)
[文档] def set_letter(self) -> None: """设置为文书体模式(高质量打印)""" self.set_quality(PrintQuality.LETTER)
[文档] def set_font(self, font: Font) -> None: """设置西文字体 (ESC k n) Args: font: 字体枚举值 (ROMAN/SANS_SERIF/COURIER/PRESTIGE/SCRIPT) """ self._append(LQ635KIICommand.FONT_ROMAN + bytes([font.value]))
[文档] def set_pitch(self, cpi: int) -> None: """设置字符间距 (CPI) Args: cpi: 每英寸字符数,支持 10/12/15 """ if cpi == 10: self._append(LQ635KIICommand.PITCH_10CPI) elif cpi == 12: self._append(LQ635KIICommand.PITCH_12CPI) elif cpi == 15: self._append(LQ635KIICommand.PITCH_15CPI)
[文档] def set_proportional(self, on: bool = True) -> None: """设置比例间距模式 比例模式下每个字符宽度不同(如 i 比 W 窄),关闭后恢复等宽模式。 Args: on: True 开启比例间距,False 关闭 """ if on: self._append(LQ635KIICommand.PROPORTIONAL_ON) else: self._append(LQ635KIICommand.PROPORTIONAL_OFF)
[文档] def set_character_space(self, n: int) -> None: """设置字符间距 (ESC SP n) Args: n: 间距值,单位 n/120 英寸 (0-127) """ self._append(LQ635KIICommand.CHARACTER_SPACE + bytes([min(n, 127) & 0xFF]))
# ============================================================ # 放大缩小 # ============================================================
[文档] def set_double_width(self, on: bool = True) -> None: """倍宽打印 (ESC W) Args: on: True 开启倍宽,False 恢复正常宽度 """ if on: self._append(LQ635KIICommand.DOUBLE_WIDTH_ON) else: self._append(LQ635KIICommand.DOUBLE_WIDTH_OFF)
[文档] def set_double_height(self, on: bool = True) -> None: """倍高打印 (ESC w) Args: on: True 开启倍高,False 恢复正常高度 """ if on: self._append(LQ635KIICommand.DOUBLE_HEIGHT_ON) else: self._append(LQ635KIICommand.DOUBLE_HEIGHT_OFF)
[文档] def set_condensed(self, on: bool = True) -> None: """压缩打印模式(约 17.5 CPI) (SI / DC2) Args: on: True 开启压缩模式,False 关闭 """ if on: self._append(LQ635KIICommand.CONDENSED_ON) else: self._append(LQ635KIICommand.CONDENSED_OFF)
[文档] def set_single_line_width(self, on: bool = True) -> None: """单行倍宽 (SO / DC4) 仅对当前行有效,换行后自动恢复。 Args: on: True 开启单行倍宽,False 关闭 """ if on: self._append(LQ635KIICommand.SINGLE_LINE_WIDTH) else: self._append(LQ635KIICommand.SINGLE_LINE_WIDTH_OFF)
# ============================================================ # 特殊效果 # ============================================================
[文档] def set_bold(self, on: bool = True) -> None: """粗体效果 (ESC E / ESC F) Args: on: True 开启粗体,False 关闭 """ if on: self._append(LQ635KIICommand.BOLD_ON) else: self._append(LQ635KIICommand.BOLD_OFF)
[文档] def set_italic(self, on: bool = True) -> None: """斜体效果 (ESC 4 / ESC 5) Args: on: True 开启斜体,False 关闭 """ if on: self._append(LQ635KIICommand.ITALIC_ON) else: self._append(LQ635KIICommand.ITALIC_OFF)
[文档] def set_underline(self, on: bool = True) -> None: """下划线效果 (ESC - 1 / ESC - 0) Args: on: True 开启下划线,False 关闭 """ if on: self._append(LQ635KIICommand.UNDERLINE_ON) else: self._append(LQ635KIICommand.UNDERLINE_OFF)
[文档] def set_double_strike(self, on: bool = True) -> None: """重叠打印效果(加深颜色) (ESC G / ESC H) 打印机在同一位置重复打印两次以加深颜色。 Args: on: True 开启重叠打印,False 关闭 """ if on: self._append(LQ635KIICommand.DOUBLE_STRIKE_ON) else: self._append(LQ635KIICommand.DOUBLE_STRIKE_OFF)
[文档] def set_superscript(self) -> None: """设置为上标模式 (ESC S 0)""" self._append(LQ635KIICommand.SUPERSCRIPT)
[文档] def set_subscript(self) -> None: """设置为下标模式 (ESC S 1)""" self._append(LQ635KIICommand.SUBSCRIPT)
[文档] def cancel_super_sub(self) -> None: """取消上标/下标模式 (ESC T)""" self._append(LQ635KIICommand.SUPERSUB_OFF)
[文档] def set_print_mode(self, mode_byte: int) -> None: """组合打印模式 (ESC ! n) Args: mode_byte: 模式字节,各bit含义: bit0: 10cpi (0) / 12cpi (1) bit1: 压缩打印 bit2: 粗体 bit3: 倍宽 bit4: 斜体 bit5: 下划线 bit6: 上标/下标 bit7: 倍高 """ self._append(LQ635KIICommand.PRINT_MODE + bytes([mode_byte & 0xFF]))
# ============================================================ # 定位(核心:X 绝对 + Y 相对) # ============================================================
[文档] def set_absolute_x(self, x_60inch: int) -> None: """绝对水平定位 (ESC $ nL nH) Args: x_60inch: 水平位置,单位 1/60 英寸 """ nl = x_60inch & 0xFF nh = (x_60inch >> 8) & 0xFF self._append(LQ635KIICommand.ABSOLUTE_HORIZONTAL + bytes([nl, nh]))
[文档] def set_absolute_x_mm(self, x_mm: float) -> None: """绝对水平定位(毫米单位)- 自动叠加打印机物理校准偏移 Args: x_mm: 水平位置,单位毫米(模板/字段逻辑坐标,不含物理校准) """ # _printer_cal_x_offset 为打印机硬件校准,对全部文字统一叠加; # 英制字段的额外偏移已在调用方 x_mm 参数中预先加好(见 PrinterService)。 actual_x = x_mm + self._printer_cal_x_offset x_60inch = int(actual_x * 60 / 25.4) self.set_absolute_x(x_60inch)
[文档] def move_y_relative(self, dots: int) -> None: """向下移动 (ESC J n) - 单位 1/180 英寸""" n = min(max(dots, 0), 255) self._append(LQ635KIICommand.RELATIVE_VERTICAL_MOVE + bytes([n]))
[文档] def move_y_mm(self, delta_mm: float) -> None: """向下移动指定毫米(相对移动)""" delta_dots = int(delta_mm * 180 / 25.4) if delta_dots > 0: self.move_y_relative(delta_dots) self._current_y_mm += delta_mm
[文档] def set_absolute_y_mm(self, y_mm: float) -> None: """绝对垂直定位(毫米单位)- 使用相对移动实现 由于 ESC ( V 无效,改用 ESC J 相对移动, 从当前位置移动到目标 Y 位置。 """ # 应用 Y 偏移补偿 target_y = y_mm + self._y_offset delta_y = target_y - self._current_y_mm if delta_y > 0: self.move_y_mm(delta_y) elif delta_y < 0: # 向上移动(使用 ESC T,有限支持) delta_dots = int(abs(delta_y) * 180 / 25.4) self.move_y_up(min(delta_dots, 255)) self._current_y_mm = target_y
# 注意:向下移动后,_current_y_mm 在 move_y_mm 中已更新 # 向上移动后需要手动更新
[文档] def set_absolute_xy_mm(self, x_mm: float, y_mm: float) -> None: """同时设置 X,Y 绝对位置(推荐方法) Args: x_mm: X 坐标(毫米) y_mm: Y 坐标(毫米) """ self.set_absolute_x_mm(x_mm) self.set_absolute_y_mm(y_mm)
[文档] def move_y_up(self, dots: int) -> None: """向上移动 (ESC T n) - 单位 1/180 英寸""" n = min(max(dots, 0), 255) self._append(LQ635KIICommand.REVERSE_VERTICAL_MOVE + bytes([n]))
[文档] def move_x_relative(self, dots: int) -> None: r"""相对水平移动 (ESC \ nL nH) - 备用,一般用绝对定位 Args: dots: 移动点数,单位 1/180 英寸 (正数右移,负数左移) """ if dots >= 0: nl = dots & 0xFF nh = (dots >> 8) & 0xFF else: n = 65536 + dots nl = n & 0xFF nh = (n >> 8) & 0xFF self._append(LQ635KIICommand.RELATIVE_HORIZONTAL + bytes([nl, nh]))
# 弃用方法(保留但不推荐使用,因为 ESC ( V 无效)
[文档] def set_absolute_y(self, y_180inch: int) -> None: """绝对垂直定位 (ESC ( V) - 已弃用,命令无效""" pass
# ============================================================ # 字符表选择 # ============================================================
[文档] def set_character_table(self, table: int) -> None: """选择字符表 (ESC t n) Args: table: 0=斜体表, 1=图形表, 2=下载字符表 """ self._append(LQ635KIICommand.CHARACTER_TABLE_ITALIC + bytes([table & 0x03]))
[文档] def set_italic_table(self) -> None: """选择斜体字符表""" self._append(LQ635KIICommand.CHARACTER_TABLE_ITALIC)
[文档] def set_graphics_table(self) -> None: """选择图形字符表""" self._append(LQ635KIICommand.CHARACTER_TABLE_GRAPHICS)
# ============================================================ # 文本打印 # ============================================================
[文档] def print_text(self, text: str) -> None: """打印文本 LQ-635KII 自动识别中文字符,直接发送 GB2312/GB18030 编码即可。 Args: text: 要打印的文本内容 """ try: # 使用 GB18030 编码 (兼容 GB2312,支持更多汉字) self._append(text.encode("gb18030", errors="ignore")) except Exception: self._append(text.encode("gbk", errors="ignore"))
[文档] def println(self, text: str = "") -> None: """打印一行文本并换行 Args: text: 要打印的文本内容,默认为空(仅换行) """ if text: self.print_text(text) self._append(LQ635KIICommand.LF) self._current_y_mm += self._line_spacing_mm # 1/6 英寸 ≈ 4.23mm
[文档] def print_line(self, text: str) -> None: """打印一行文本并换行(println 的别名) Args: text: 要打印的文本内容 """ self.println(text)
[文档] def newline(self) -> None: """发送换行命令 (LF)""" self._append(LQ635KIICommand.LF) self._current_y_mm += self._line_spacing_mm
# ============================================================ # 图形打印 # ============================================================
[文档] def print_graphics_8pin(self, data: bytes, width: int, mode: str = "single") -> None: """8针图形打印 Args: data: 图形数据 (每列1字节) width: 宽度 (列数) mode: 模式,可选: - "single": 60 DPI - "double": 120 DPI - "hs_double": 120 DPI 高速 - "quad": 240 DPI """ nl = width & 0xFF nh = (width >> 8) & 0xFF if mode == "double": cmd = LQ635KIICommand.GRAPHICS_8PIN_DOUBLE elif mode == "hs_double": cmd = LQ635KIICommand.GRAPHICS_8PIN_HS_DOUBLE elif mode == "quad": cmd = LQ635KIICommand.GRAPHICS_8PIN_QUAD else: cmd = LQ635KIICommand.GRAPHICS_8PIN_SINGLE self._append(cmd + bytes([nl, nh]) + data)
[文档] def print_graphics_24pin( self, data: bytes, width: int, dpi: PrinterDPI = PrinterDPI.DPI_180 ) -> None: """24针图形打印 Args: data: 图形数据 (每列3字节) width: 宽度 (列数) dpi: 分辨率,可选 DPI_60/DPI_90/DPI_120/DPI_180/DPI_360 """ nl = width & 0xFF nh = (width >> 8) & 0xFF if dpi == PrinterDPI.DPI_60: cmd = LQ635KIICommand.GRAPHICS_24PIN_SINGLE elif dpi == PrinterDPI.DPI_90: cmd = LQ635KIICommand.GRAPHICS_24PIN_CRT elif dpi == PrinterDPI.DPI_120: cmd = LQ635KIICommand.GRAPHICS_24PIN_DOUBLE elif dpi == PrinterDPI.DPI_360: cmd = LQ635KIICommand.GRAPHICS_24PIN_HEX else: cmd = LQ635KIICommand.GRAPHICS_24PIN_TRIPLE self._append(cmd + bytes([nl, nh]) + data)
[文档] def redefine_graphics(self, target: bytes, source: bytes) -> None: """重新定义图形模式 (ESC ? s m) 将标准图形命令重映射到 ESC * 的某个模式。 Args: target: 要重定义的命令 (如 b"K", b"L", b"Y", b"Z") source: 源模式 (如 b"\x20", b"\x21", b"\x26", b"\x27", b"\x28") """ self._append(LQ635KIICommand.REDEFINE_GRAPHICS + target + source)
# ============================================================ # 制表位 # ============================================================
[文档] def set_horizontal_tabs(self, tabs: list[int]) -> None: """设置水平制表位 (ESC D) Args: tabs: 制表位列位置列表,以0结尾 """ data = LQ635KIICommand.SET_HORIZONTAL_TABS for tab in tabs: data += bytes([tab]) data += b"\x00" self._append(data)
[文档] def tab_horizontal(self) -> None: """跳转到下一个水平制表位 (HT)""" self._append(LQ635KIICommand.HORIZONTAL_TAB)
[文档] def set_vertical_tabs(self, tabs: list[int]) -> None: """设置垂直制表位 (ESC B) Args: tabs: 制表位行位置列表,以0结尾 """ data = LQ635KIICommand.SET_VERTICAL_TABS for tab in tabs: data += bytes([tab]) data += b"\x00" self._append(data)
[文档] def tab_vertical(self) -> None: """跳转到下一个垂直制表位 (VT)""" self._append(LQ635KIICommand.VERTICAL_TAB)
[文档] def set_vertical_tabs_channel(self, channel: int, tabs: list[int]) -> None: """设置垂直制表位通道 (ESC b c n1 n2...0) Args: channel: 通道号 (0-7) tabs: 制表位行位置列表 """ data = LQ635KIICommand.SET_VERTICAL_TABS_CHANNEL + bytes([channel]) for tab in tabs: data += bytes([tab]) data += b"\x00" self._append(data)
[文档] def select_vfu_channel(self, channel: int) -> None: """选择 VFU 通道 (ESC / c) Args: channel: 通道号 (0-7) """ self._append(LQ635KIICommand.SELECT_VFU_CHANNEL + bytes([channel & 0x07]))
# ============================================================ # 高级功能 # ============================================================
[文档] def print_barcode(self, code: str, barcode_type: int = 0) -> None: """打印条码 注意:此功能需要打印机固件支持条码命令。 Args: code: 条码内容 barcode_type: 条码类型编号 (需要打印机支持) """ # 使用 ESC (B 命令 (需要确认打印机支持) self._append(LQ635KIICommand.ESC + b"(B" + bytes([len(code) & 0xFF, 0, barcode_type])) self._append(code.encode("ascii"))