각 영수증에 "*** 방법 1 ***" 형식으로 번호 표시
어떤 방법이 QR을 성공적으로 인쇄했는지 쉽게 확인 가능
출력 예:
================================
*** 방법 1 ***
내장 QR (GS ( k)
================================
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
299 lines
8.7 KiB
Python
299 lines
8.7 KiB
Python
"""
|
|
ESC/POS QR 코드 인쇄 방식 테스트
|
|
여러 가지 방법을 한 번에 시도하여 어떤 방식이 작동하는지 확인
|
|
"""
|
|
|
|
import socket
|
|
import qrcode
|
|
import time
|
|
from PIL import Image
|
|
|
|
# 프린터 설정 (고정)
|
|
PRINTER_IP = "192.168.0.174"
|
|
PRINTER_PORT = 9100
|
|
|
|
# 테스트 URL (짧은 버전)
|
|
TEST_URL = "https://mile.0bin.in/test"
|
|
|
|
|
|
def send_to_printer(data, method_name):
|
|
"""프린터로 데이터 전송"""
|
|
try:
|
|
print(f"\n{'='*60}")
|
|
print(f"[{method_name}] 전송 시작...")
|
|
print(f"데이터 크기: {len(data)} bytes")
|
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(10)
|
|
sock.connect((PRINTER_IP, PRINTER_PORT))
|
|
sock.sendall(data)
|
|
sock.close()
|
|
|
|
print(f"[{method_name}] ✅ 전송 완료!")
|
|
time.sleep(2) # 프린터 처리 대기
|
|
return True
|
|
except Exception as e:
|
|
print(f"[{method_name}] ❌ 실패: {e}")
|
|
return False
|
|
|
|
|
|
def method_1_native_qr_model2():
|
|
"""
|
|
방법 1: 프린터 내장 QR 생성 (GS ( k) - Model 2
|
|
가장 안정적이지만 프린터 지원 필요
|
|
"""
|
|
ESC = b'\x1b'
|
|
GS = b'\x1d'
|
|
|
|
commands = []
|
|
|
|
# 초기화
|
|
commands.append(ESC + b'@')
|
|
|
|
# 헤더
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append("================================\n".encode('euc-kr'))
|
|
commands.append(" *** 방법 1 ***\n".encode('euc-kr'))
|
|
commands.append(" 내장 QR (GS ( k)\n".encode('euc-kr'))
|
|
commands.append("================================\n".encode('euc-kr'))
|
|
commands.append("\n".encode('euc-kr'))
|
|
|
|
# QR 설정
|
|
# GS ( k pL pH cn fn n (QR Code)
|
|
# cn = 49 (Model 1/2 선택)
|
|
# fn = 65 (모델 선택)
|
|
# n = 50 (Model 2)
|
|
|
|
# 모델 설정
|
|
commands.append(GS + b'(k' + bytes([3, 0, 49, 65, 50])) # Model 2
|
|
|
|
# 에러 정정 레벨 설정 (fn=69, n=48=L)
|
|
commands.append(GS + b'(k' + bytes([3, 0, 49, 69, 48]))
|
|
|
|
# 모듈 크기 설정 (fn=67, n=8)
|
|
commands.append(GS + b'(k' + bytes([3, 0, 49, 67, 8]))
|
|
|
|
# QR 데이터 저장 (fn=80)
|
|
qr_data = TEST_URL.encode('utf-8')
|
|
data_len = len(qr_data) + 3
|
|
pL = data_len & 0xFF
|
|
pH = (data_len >> 8) & 0xFF
|
|
commands.append(GS + b'(k' + bytes([pL, pH, 49, 80, 48]) + qr_data)
|
|
|
|
# QR 인쇄 (fn=81)
|
|
commands.append(GS + b'(k' + bytes([3, 0, 49, 81, 48]))
|
|
|
|
# 푸터
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append(f"URL: {TEST_URL}\n".encode('euc-kr'))
|
|
commands.append("\n\n\n".encode('euc-kr'))
|
|
|
|
# 용지 커트
|
|
commands.append(GS + b'V' + bytes([1]))
|
|
|
|
return b''.join(commands)
|
|
|
|
|
|
def method_2_raster_bitmap_gs_v():
|
|
"""
|
|
방법 2: Raster Bit Image (GS v 0)
|
|
"""
|
|
ESC = b'\x1b'
|
|
GS = b'\x1d'
|
|
|
|
commands = []
|
|
|
|
# 초기화
|
|
commands.append(ESC + b'@')
|
|
|
|
# 헤더
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append("================================\n".encode('euc-kr'))
|
|
commands.append(" *** 방법 2 ***\n".encode('euc-kr'))
|
|
commands.append(" Raster (GS v 0)\n".encode('euc-kr'))
|
|
commands.append("================================\n".encode('euc-kr'))
|
|
commands.append("\n".encode('euc-kr'))
|
|
|
|
# QR 이미지 생성 (작게: 80x80)
|
|
qr = qrcode.QRCode(version=1, box_size=2, border=2)
|
|
qr.add_data(TEST_URL)
|
|
qr.make(fit=True)
|
|
qr_image = qr.make_image(fill_color="black", back_color="white")
|
|
qr_image = qr_image.resize((80, 80))
|
|
|
|
# 1비트 흑백으로 변환
|
|
qr_image = qr_image.convert('1')
|
|
width, height = qr_image.size
|
|
pixels = qr_image.load()
|
|
|
|
# GS v 0 명령어
|
|
width_bytes = (width + 7) // 8
|
|
commands.append(GS + b'v0' + bytes([0])) # 보통 모드
|
|
commands.append(bytes([width_bytes & 0xFF, (width_bytes >> 8) & 0xFF])) # xL, xH
|
|
commands.append(bytes([height & 0xFF, (height >> 8) & 0xFF])) # yL, yH
|
|
|
|
# 이미지 데이터
|
|
for y in range(height):
|
|
for x in range(0, width, 8):
|
|
byte = 0
|
|
for bit in range(8):
|
|
if x + bit < width:
|
|
if pixels[x + bit, y] == 0: # 검은색
|
|
byte |= (1 << (7 - bit))
|
|
commands.append(bytes([byte]))
|
|
|
|
# 푸터
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append(f"URL: {TEST_URL}\n".encode('euc-kr'))
|
|
commands.append("\n\n\n".encode('euc-kr'))
|
|
|
|
# 용지 커트
|
|
commands.append(GS + b'V' + bytes([1]))
|
|
|
|
return b''.join(commands)
|
|
|
|
|
|
def method_3_bit_image_esc_star():
|
|
"""
|
|
방법 3: Bit Image (ESC *) - 24-dot double-density
|
|
현재 사용 중인 방식
|
|
"""
|
|
ESC = b'\x1b'
|
|
GS = b'\x1d'
|
|
|
|
commands = []
|
|
|
|
# 초기화
|
|
commands.append(ESC + b'@')
|
|
|
|
# 헤더
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append("================================\n".encode('euc-kr'))
|
|
commands.append(" *** 방법 3 ***\n".encode('euc-kr'))
|
|
commands.append(" Bit Image (ESC *)\n".encode('euc-kr'))
|
|
commands.append("================================\n".encode('euc-kr'))
|
|
commands.append("\n".encode('euc-kr'))
|
|
|
|
# QR 이미지 생성 (작게: 80x80)
|
|
qr = qrcode.QRCode(version=1, box_size=2, border=2)
|
|
qr.add_data(TEST_URL)
|
|
qr.make(fit=True)
|
|
qr_image = qr.make_image(fill_color="black", back_color="white")
|
|
qr_image = qr_image.resize((80, 80))
|
|
|
|
# 1비트 흑백으로 변환
|
|
qr_image = qr_image.convert('1')
|
|
width, height = qr_image.size
|
|
pixels = qr_image.load()
|
|
|
|
# ESC * 명령어로 라인별 인쇄
|
|
for y in range(0, height, 24):
|
|
line_height = min(24, height - y)
|
|
|
|
# ESC * m nL nH
|
|
nL = width & 0xFF
|
|
nH = (width >> 8) & 0xFF
|
|
commands.append(ESC + b'*' + bytes([33, nL, nH])) # m=33 (24-dot double-density)
|
|
|
|
# 라인 데이터
|
|
for x in range(width):
|
|
byte1, byte2, byte3 = 0, 0, 0
|
|
|
|
for bit in range(line_height):
|
|
pixel_y = y + bit
|
|
if pixel_y < height:
|
|
if pixels[x, pixel_y] == 0: # 검은색
|
|
if bit < 8:
|
|
byte1 |= (1 << (7 - bit))
|
|
elif bit < 16:
|
|
byte2 |= (1 << (15 - bit))
|
|
else:
|
|
byte3 |= (1 << (23 - bit))
|
|
|
|
commands.append(bytes([byte1, byte2, byte3]))
|
|
|
|
commands.append(b'\n')
|
|
|
|
# 푸터
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append(f"URL: {TEST_URL}\n".encode('euc-kr'))
|
|
commands.append("\n\n\n".encode('euc-kr'))
|
|
|
|
# 용지 커트
|
|
commands.append(GS + b'V' + bytes([1]))
|
|
|
|
return b''.join(commands)
|
|
|
|
|
|
def method_4_simple_text_only():
|
|
"""
|
|
방법 4: 텍스트만 (비교용)
|
|
"""
|
|
ESC = b'\x1b'
|
|
GS = b'\x1d'
|
|
|
|
commands = []
|
|
|
|
# 초기화
|
|
commands.append(ESC + b'@')
|
|
|
|
# 헤더
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append("================================\n".encode('euc-kr'))
|
|
commands.append(" *** 방법 4 ***\n".encode('euc-kr'))
|
|
commands.append(" 텍스트만 (비교용)\n".encode('euc-kr'))
|
|
commands.append("================================\n".encode('euc-kr'))
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append("QR 이미지 대신 URL만 출력\n".encode('euc-kr'))
|
|
commands.append("\n".encode('euc-kr'))
|
|
commands.append(f"URL: {TEST_URL}\n".encode('euc-kr'))
|
|
commands.append("\n\n\n".encode('euc-kr'))
|
|
|
|
# 용지 커트
|
|
commands.append(GS + b'V' + bytes([1]))
|
|
|
|
return b''.join(commands)
|
|
|
|
|
|
def main():
|
|
"""메인 실행"""
|
|
print("="*60)
|
|
print("ESC/POS QR 코드 인쇄 방식 테스트")
|
|
print("="*60)
|
|
print(f"프린터: {PRINTER_IP}:{PRINTER_PORT}")
|
|
print(f"테스트 URL: {TEST_URL}")
|
|
print("="*60)
|
|
|
|
methods = [
|
|
("방법 1: 프린터 내장 QR (GS ( k)", method_1_native_qr_model2),
|
|
("방법 2: Raster Bitmap (GS v 0)", method_2_raster_bitmap_gs_v),
|
|
("방법 3: Bit Image (ESC *)", method_3_bit_image_esc_star),
|
|
("방법 4: 텍스트만", method_4_simple_text_only),
|
|
]
|
|
|
|
results = []
|
|
|
|
for name, method_func in methods:
|
|
try:
|
|
data = method_func()
|
|
success = send_to_printer(data, name)
|
|
results.append((name, success))
|
|
except Exception as e:
|
|
print(f"[{name}] ❌ 함수 실행 오류: {e}")
|
|
results.append((name, False))
|
|
|
|
# 결과 요약
|
|
print("\n" + "="*60)
|
|
print("테스트 결과 요약")
|
|
print("="*60)
|
|
for name, success in results:
|
|
status = "✅ 성공" if success else "❌ 실패"
|
|
print(f"{name}: {status}")
|
|
|
|
print("\n인쇄된 영수증을 확인하여 어떤 방법이 QR을 제대로 출력했는지 확인하세요!")
|
|
print("="*60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|