certflow.services.printer.print_strategy 源代码

# src/certflow/services/printer/print_strategy.py
"""打印策略接口 - 支持 GDI 和 ESC/P-K 切换

允许通过 PrintConfig 配置切换打印引擎,业务层无需修改代码。
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Protocol

if TYPE_CHECKING:
    from certflow.config.print_config import RuntimePrintConfig
    from certflow.services.printer.printer_service import PrinterService


[文档] class PrintStrategy(Protocol): """打印策略协议"""
[文档] def print_certificate( self, data: dict[str, Any], serial_number: str, copies: int = 1, config: RuntimePrintConfig | None = None, ) -> bool: """打印合格证""" ...
[文档] class GDIPrintStrategy: """GDI 打印策略(原有实现) 使用 win32ui GDI 进行精确坐标打印,适用于预印卡纸。 """ def __init__(self, printer_name: str = "") -> None: self.printer_name = printer_name self._last_os_job_id: int | None = None
[文档] def print_certificate( self, data: dict[str, Any], serial_number: str, copies: int = 1, config: RuntimePrintConfig | None = None, ) -> bool: """调用原有 GDI 打印逻辑""" from certflow.services.cert_print_engine import CertPrintEngine engine = CertPrintEngine(self.printer_name) ok = engine._print_single_gdi(data, serial_number, self.printer_name) self._last_os_job_id = engine.last_os_job_id return ok
@property def last_os_job_id(self) -> int | None: """最近一次 GDI 打印提交的 OS 作业 ID(由 CertPrintEngine 捕获)。""" return self._last_os_job_id
[文档] class ESCPPrintStrategy: """ESC/P-K 打印策略(新驱动) 使用 ESC/P-K 命令直接控制 LQ-635KII 打印机, 坐标从 PrintConfig.template.fields 获取。 """ def __init__(self, printer_name: str = "") -> None: self.printer_name = printer_name self._service = None def _get_service(self) -> PrinterService | None: if self._service is None: from .printer_service import PrinterService self._service = PrinterService(self.printer_name or None) return self._service @property def last_os_job_id(self) -> int | None: """最近一次 ESC/P-K 打印提交的 OS 作业 ID(委托 PrinterService)。""" return self._service.last_os_job_id if self._service is not None else None
[文档] def print_certificate( self, data: dict[str, Any], serial_number: str, copies: int = 1, config: RuntimePrintConfig | None = None, ) -> bool: """使用 RuntimePrintConfig 驱动的精确定位打印。 队列路径(config=None)在此构建 RuntimePrintConfig:读 ``coordinates.yaml`` 逐字段坐标 + ``print_layout`` 用户覆盖(见 ``print_config.from_template``), 使实打与预览/配置同源,不再使用硬编码 ``positions``(蓝图 §13 D1/D3 修复)。 """ from certflow.config.print_config import RuntimePrintConfig as _RuntimePrintConfig from certflow.services.printer.template_field_formatter import ( family_of, get_field_formatter, resolve_template_key, ) service = self._get_service() cert_data = { "product_name": data.get("product_name", ""), "product_model": data.get("product_model", ""), "dn": data.get("dn", ""), "pn": data.get("pn", ""), "temperature": data.get("temperature", ""), "medium": data.get("medium", ""), "check_standard": data.get("check_standard", ""), "inspector_id": data.get("inspector_id", ""), "manufacture_date": data.get("manufacture_date", ""), "cert_number": serial_number, } # 英制字段 X 偏移(mm):优先用调用方透传的 data["_x_offsets"]; # 队列路径 data 无此键时,按模板语言族现场计算(与 PrintView._format_print_data 同源)。 # 注意:这是「英制字段相对模板基点的额外右移」,与 # LQ635KIIPrinter._printer_cal_x_offset(打印机物理校准,对所有文字统一叠加) # 语义不同、相互独立;两者在 set_absolute_x_mm 内叠加生效,均为正值向右。 x_offsets = data.get("_x_offsets") if isinstance(data, dict) else None if not x_offsets and config is None: template_key = resolve_template_key( data.get("template_type") or data.get("template_name") ) family = family_of(template_key) formatter = get_field_formatter() off: dict[str, float] = {} for f in ("pn", "temperature", "medium", "dn"): raw = data.get(f) if raw: _, xo = formatter.format(f, raw, family) if xo: off[f] = xo x_offsets = off or None if config is None: template_key = resolve_template_key( data.get("template_type") or data.get("template_name") ) config = _RuntimePrintConfig.from_template(template_key, printer_name=self.printer_name) return service.print_by_template_config(config, cert_data, copies, x_offsets=x_offsets)
[文档] class PrintStrategyFactory: """打印策略工厂 根据配置返回对应的打印策略实例。 """
[文档] @staticmethod def create(engine: str = "escp", printer_name: str = "") -> PrintStrategy: """创建打印策略 Args: engine: 引擎类型 ("gdi" 或 "escp") printer_name: 打印机名称,为空则使用默认打印机 Returns: 打印策略实例 """ if engine == "gdi": return GDIPrintStrategy(printer_name) return ESCPPrintStrategy(printer_name)
[文档] @staticmethod def from_config( printer_name: str = "", config: RuntimePrintConfig | None = None ) -> PrintStrategy: """从 RuntimePrintConfig 或 config.yaml 读取引擎类型并创建策略 Args: printer_name: 打印机名称,为空则使用默认打印机 config: RuntimePrintConfig 实例,优先使用;为 None 时回退到 config.yaml Returns: 打印策略实例 """ engine = "escp" if config: engine = config.engine else: try: from certflow.config.settings import cfg engine = cfg("print.engine", "escp") except Exception: pass return PrintStrategyFactory.create(engine, printer_name)