""" Read or change the PT-E560BT driver's print resolution. Why this exists: the Brother driver offers two resolution modes 180 x 180 dpi balanced, matches the 128-dot head in both axes 180 x 360 dpi anisotropic - 2x the resolution along the tape feed In the anisotropic mode the driver reports the media as 24 x 65 mm but rasters along the tape at 360 dpi, so a page authored for the media dimensions can be scaled by the wrong factor and printed short. This tool lets the mode be switched from the command line so the effect can be tested without clicking through the printer properties dialog. Changing the resolution modifies a system printer default, so it is reversible: use --restore, or simply re-run with the other value. Administrator rights are required; without them the script reports what to change by hand. Usage: python set_printer_resolution.py # show current setting python set_printer_resolution.py --mode 180 # symmetric 180x180 python set_printer_resolution.py --mode 360 # anisotropic 180x360 python set_printer_resolution.py --restore # put back the old value """ from __future__ import annotations import argparse import json import os import sys PRINTER = "Brother PT-E560BT" STATE_FILE = ".printer_resolution_backup.json" # DEVMODE field flags DM_PRINTQUALITY = 0x00000400 DM_YRESOLUTION = 0x00040000 DM_MODIFY = 0x00000008 DM_IN_BUFFER = 0x00000008 DM_OUT_BUFFER = 0x00000002 def _devmode_field_bits(): return DM_PRINTQUALITY | DM_YRESOLUTION def current_settings(): """Return the printer's current resolution-related DEVMODE values.""" import win32print handle = win32print.OpenPrinter(PRINTER) try: info = win32print.GetPrinter(handle, 2) dm = info["pDevMode"] return { "port": info.get("pPortName"), "paper_size": dm.PaperSize, "paper_width": dm.PaperWidth, "paper_length": dm.PaperLength, "orientation": dm.Orientation, "print_quality": dm.PrintQuality, "y_resolution": dm.YResolution, "fields": dm.Fields, } finally: win32print.ClosePrinter(handle) def measure_dc(): """Read the resolution and paper dimensions the driver reports via a DC.""" try: import win32ui except ImportError: return None try: dc = win32ui.CreateDC() dc.CreatePrinterDC(PRINTER) except Exception as exc: # noqa: BLE001 print(f" Could not create DC: {exc}") return None try: return { "logpixelsx": dc.GetDeviceCaps(88), "logpixelsy": dc.GetDeviceCaps(90), "physicalwidth": dc.GetDeviceCaps(110), "physicalheight": dc.GetDeviceCaps(111), "horzres": dc.GetDeviceCaps(8), "vertres": dc.GetDeviceCaps(10), } finally: try: dc.DeleteDC() except Exception: # noqa: BLE001 pass def show(label=""): settings = current_settings() caps = measure_dc() print(f"\n{label}") print(f" DEVMODE PrintQuality : {settings['print_quality']}") print(f" DEVMODE YResolution : {settings['y_resolution']}") print(f" DEVMODE PaperSize : {settings['paper_size']} " f"({settings['paper_width']/10:.1f} x {settings['paper_length']/10:.1f} mm)") if caps: x, y = caps["logpixelsx"], caps["logpixelsy"] if x and y: print(f" DC resolution : {x} x {y} dpi", end="") if max(x, y) / min(x, y) >= 1.9: print(" <-- ANISOTROPIC (2x mismatch between axes)") else: print(" (symmetric)") print(f" Physical page : {caps['physicalwidth']} x " f"{caps['physicalheight']} dots = " f"{caps['physicalwidth']/max(x,1)*25.4:.2f} x " f"{caps['physicalheight']/max(y,1)*25.4:.2f} mm") print(f" Printable area : {caps['horzres']} x {caps['vertres']} dots") return settings def apply_mode(mode: str) -> bool: """Set the driver resolution mode. mode is '180' or '360'.""" import win32print y_res = 360 if mode == "360" else 0 try: handle = win32print.OpenPrinter( PRINTER, {"DesiredAccess": win32print.PRINTER_ALL_ACCESS} ) except Exception as exc: # noqa: BLE001 print(f"Cannot open printer for writing: {exc}") print("Re-run this from an elevated (Administrator) terminal.") return False try: info = win32print.GetPrinter(handle, 2) dm = info["pDevMode"] dm.PrintQuality = 180 dm.YResolution = y_res dm.Fields |= _devmode_field_bits() info["pDevMode"] = dm win32print.SetPrinter(handle, 2, info, 0) print(f"Applied resolution mode {mode} (YResolution={y_res}).") return True except Exception as exc: # noqa: BLE001 print(f"Failed to set resolution: {exc}") print("This normally needs Administrator rights.") return False finally: win32print.ClosePrinter(handle) def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--mode", choices=["180", "360"], help="resolution mode to set") parser.add_argument("--restore", action="store_true", help="restore the saved value") args = parser.parse_args() before = show("BEFORE") if args.restore: if not os.path.exists(STATE_FILE): print(f"\nNo backup found at {STATE_FILE}; nothing to restore.") return 1 with open(STATE_FILE) as handle: saved = json.load(handle) mode = "360" if saved.get("y_resolution") else "180" print(f"\nRestoring YResolution={saved.get('y_resolution')} (mode {mode})") if apply_mode(mode): show("AFTER") return 0 if not args.mode: print("\n(no --mode given; nothing changed)") return 0 # Save the previous value so the change can be undone. with open(STATE_FILE, "w") as handle: json.dump(before, handle, indent=2) print(f"\nSaved previous setting to {STATE_FILE}") if apply_mode(args.mode): show("AFTER") return 0 if __name__ == "__main__": raise SystemExit(main())