diff --git a/backend/test_qr_methods.py b/backend/test_qr_methods.py new file mode 100644 index 0000000..3930ffc --- /dev/null +++ b/backend/test_qr_methods.py @@ -0,0 +1,286 @@ +""" +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("=== 방법 1: 내장 QR (GS ( k) ===\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("=== 방법 2: Raster (GS v 0) ===\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("=== 방법 3: Bit Image (ESC *) ===\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("=== 방법 4: 텍스트만 (비교용) ===\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()