From a4cfcb6bdeaf1d15afa24701308f299f60b4d0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=9C=EA=B3=A8=EC=95=BD=EC=82=AC?= Date: Thu, 29 Jan 2026 20:15:13 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20QR=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20v2?= =?UTF-8?q?=20-=20=EB=8D=94=20=EB=A7=8E=EC=9D=80=20=EB=B3=80=ED=98=95=20(?= =?UTF-8?q?=ED=81=AC=EA=B8=B0,=20=EB=B0=80=EB=8F=84,=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/test_qr_methods_v2.py | 281 ++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 backend/test_qr_methods_v2.py diff --git a/backend/test_qr_methods_v2.py b/backend/test_qr_methods_v2.py new file mode 100644 index 0000000..7b0a97a --- /dev/null +++ b/backend/test_qr_methods_v2.py @@ -0,0 +1,281 @@ +""" +ESC/POS QR 코드 인쇄 방식 테스트 v2 +더 많은 변형 시도 (크기, 밀도, 파라미터) +""" + +import socket +import qrcode +import time + +# 프린터 설정 +PRINTER_IP = "192.168.0.174" +PRINTER_PORT = 9100 + +# 테스트 URL (더 짧게) +TEST_URL = "https://bit.ly/test" + + +def send_to_printer(data, method_name): + """프린터로 데이터 전송""" + try: + print(f"\n[{method_name}] 전송 중... ({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(1.5) + return True + except Exception as e: + print(f"[{method_name}] ❌ 실패: {e}") + return False + + +def method_1_tiny_qr_escstar(): + """방법 1: 아주 작은 QR (30x30) + ESC *""" + ESC = b'\x1b' + GS = b'\x1d' + commands = [] + commands.append(ESC + b'@') + commands.append("\n================================\n".encode('euc-kr')) + commands.append(" *** 방법 1 ***\n".encode('euc-kr')) + commands.append(" 작은 QR 30x30 (ESC *)\n".encode('euc-kr')) + commands.append("================================\n\n".encode('euc-kr')) + + # 30x30 QR + qr = qrcode.QRCode(version=1, box_size=1, border=1) + qr.add_data(TEST_URL) + qr.make(fit=True) + img = qr.make_image(fill_color="black", back_color="white").resize((30, 30)).convert('1') + width, height = img.size + pixels = img.load() + + # ESC * m=0 (8-dot single-density) + for y in range(0, height, 8): + commands.append(ESC + b'*' + bytes([0, width & 0xFF, (width >> 8) & 0xFF])) + for x in range(width): + byte = 0 + for bit in range(min(8, height - y)): + if pixels[x, y + bit] == 0: + byte |= (1 << (7 - bit)) + commands.append(bytes([byte])) + commands.append(b'\n') + + commands.append(f"\nURL: {TEST_URL}\n\n\n".encode('euc-kr')) + commands.append(GS + b'V' + bytes([1])) + return b''.join(commands) + + +def method_2_medium_qr_escstar_mode32(): + """방법 2: 중간 QR (50x50) + ESC * mode 32""" + ESC = b'\x1b' + GS = b'\x1d' + commands = [] + commands.append(ESC + b'@') + commands.append("\n================================\n".encode('euc-kr')) + commands.append(" *** 방법 2 ***\n".encode('euc-kr')) + commands.append(" 중간 QR 50x50 (ESC * m=32)\n".encode('euc-kr')) + commands.append("================================\n\n".encode('euc-kr')) + + # 50x50 QR + qr = qrcode.QRCode(version=1, box_size=2, border=1) + qr.add_data(TEST_URL) + qr.make(fit=True) + img = qr.make_image(fill_color="black", back_color="white").resize((50, 50)).convert('1') + width, height = img.size + pixels = img.load() + + # ESC * m=32 (24-dot single-density) + for y in range(0, height, 24): + commands.append(ESC + b'*' + bytes([32, width & 0xFF, (width >> 8) & 0xFF])) + for x in range(width): + byte1 = byte2 = byte3 = 0 + for bit in range(min(24, height - y)): + if pixels[x, y + bit] == 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(f"\nURL: {TEST_URL}\n\n\n".encode('euc-kr')) + commands.append(GS + b'V' + bytes([1])) + return b''.join(commands) + + +def method_3_native_qr_simple(): + """방법 3: 내장 QR (더 간단한 설정)""" + ESC = b'\x1b' + GS = b'\x1d' + commands = [] + commands.append(ESC + b'@') + commands.append("\n================================\n".encode('euc-kr')) + commands.append(" *** 방법 3 ***\n".encode('euc-kr')) + commands.append(" 내장 QR 간단 설정\n".encode('euc-kr')) + commands.append("================================\n\n".encode('euc-kr')) + + qr_data = TEST_URL.encode('utf-8') + data_len = len(qr_data) + 3 + + # Model 2 + commands.append(GS + b'(k' + bytes([3, 0, 49, 65, 50])) + # Error correction L + commands.append(GS + b'(k' + bytes([3, 0, 49, 69, 48])) + # Size 4 + commands.append(GS + b'(k' + bytes([3, 0, 49, 67, 4])) + # Store data + commands.append(GS + b'(k' + bytes([data_len & 0xFF, (data_len >> 8) & 0xFF, 49, 80, 48]) + qr_data) + # Print + commands.append(GS + b'(k' + bytes([3, 0, 49, 81, 48])) + + commands.append(f"\n\nURL: {TEST_URL}\n\n\n".encode('euc-kr')) + commands.append(GS + b'V' + bytes([1])) + return b''.join(commands) + + +def method_4_native_qr_model1(): + """방법 4: 내장 QR Model 1 (구형)""" + ESC = b'\x1b' + GS = b'\x1d' + commands = [] + commands.append(ESC + b'@') + commands.append("\n================================\n".encode('euc-kr')) + commands.append(" *** 방법 4 ***\n".encode('euc-kr')) + commands.append(" 내장 QR Model 1\n".encode('euc-kr')) + commands.append("================================\n\n".encode('euc-kr')) + + qr_data = TEST_URL.encode('utf-8') + data_len = len(qr_data) + 3 + + # Model 1 (n=49) + commands.append(GS + b'(k' + bytes([3, 0, 49, 65, 49])) + # Error correction L + commands.append(GS + b'(k' + bytes([3, 0, 49, 69, 48])) + # Size 4 + commands.append(GS + b'(k' + bytes([3, 0, 49, 67, 4])) + # Store data + commands.append(GS + b'(k' + bytes([data_len & 0xFF, (data_len >> 8) & 0xFF, 49, 80, 48]) + qr_data) + # Print + commands.append(GS + b'(k' + bytes([3, 0, 49, 81, 48])) + + commands.append(f"\n\nURL: {TEST_URL}\n\n\n".encode('euc-kr')) + commands.append(GS + b'V' + bytes([1])) + return b''.join(commands) + + +def method_5_raster_tiny(): + """방법 5: Raster 초소형 (40x40)""" + ESC = b'\x1b' + GS = b'\x1d' + commands = [] + commands.append(ESC + b'@') + commands.append("\n================================\n".encode('euc-kr')) + commands.append(" *** 방법 5 ***\n".encode('euc-kr')) + commands.append(" Raster 40x40 (GS v 0)\n".encode('euc-kr')) + commands.append("================================\n\n".encode('euc-kr')) + + # 40x40 QR + qr = qrcode.QRCode(version=1, box_size=1, border=1) + qr.add_data(TEST_URL) + qr.make(fit=True) + img = qr.make_image(fill_color="black", back_color="white").resize((40, 40)).convert('1') + width, height = img.size + pixels = img.load() + + width_bytes = (width + 7) // 8 + commands.append(GS + b'v0' + bytes([0])) + commands.append(bytes([width_bytes & 0xFF, (width_bytes >> 8) & 0xFF])) + commands.append(bytes([height & 0xFF, (height >> 8) & 0xFF])) + + for y in range(height): + for x in range(0, width, 8): + byte = 0 + for bit in range(8): + if x + bit < width and pixels[x + bit, y] == 0: + byte |= (1 << (7 - bit)) + commands.append(bytes([byte])) + + commands.append(f"\n\nURL: {TEST_URL}\n\n\n".encode('euc-kr')) + commands.append(GS + b'V' + bytes([1])) + return b''.join(commands) + + +def method_6_no_align(): + """방법 6: 정렬 없이 + 작은 QR""" + ESC = b'\x1b' + GS = b'\x1d' + commands = [] + commands.append(ESC + b'@') + # 정렬 명령 없음! + commands.append("\n================================\n".encode('euc-kr')) + commands.append(" *** 방법 6 ***\n".encode('euc-kr')) + commands.append(" 정렬 없음 + QR 35x35\n".encode('euc-kr')) + commands.append("================================\n\n".encode('euc-kr')) + + # 35x35 QR + qr = qrcode.QRCode(version=1, box_size=1, border=1) + qr.add_data(TEST_URL) + qr.make(fit=True) + img = qr.make_image(fill_color="black", back_color="white").resize((35, 35)).convert('1') + width, height = img.size + pixels = img.load() + + # ESC * m=1 (8-dot double-density) + for y in range(0, height, 8): + commands.append(ESC + b'*' + bytes([1, width & 0xFF, (width >> 8) & 0xFF])) + for x in range(width): + byte = 0 + for bit in range(min(8, height - y)): + if pixels[x, y + bit] == 0: + byte |= (1 << (7 - bit)) + commands.append(bytes([byte])) + commands.append(b'\n') + + commands.append(f"\nURL: {TEST_URL}\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 테스트 v2 - 더 많은 변형") + print("="*60) + print(f"프린터: {PRINTER_IP}:{PRINTER_PORT}") + print(f"테스트 URL: {TEST_URL}") + print("="*60) + + methods = [ + ("방법 1: 30x30 ESC * m=0", method_1_tiny_qr_escstar), + ("방법 2: 50x50 ESC * m=32", method_2_medium_qr_escstar_mode32), + ("방법 3: 내장 QR 간단", method_3_native_qr_simple), + ("방법 4: 내장 QR Model 1", method_4_native_qr_model1), + ("방법 5: 40x40 Raster", method_5_raster_tiny), + ("방법 6: 정렬 없음 35x35", method_6_no_align), + ] + + 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: + print(f"{name}: {'✅' if success else '❌'}") + + print("\n6장의 영수증이 나옵니다. QR이 보이는 번호를 알려주세요!") + print("="*60) + + +if __name__ == "__main__": + main()