File size: 31,504 Bytes
7c5d3b3
 
 
 
31f4117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7c5d3b3
 
9e2500c
 
057c155
9e2500c
 
 
 
31f4117
 
c5d7fde
 
9e2500c
c5d7fde
9e2500c
 
 
 
 
 
c5d7fde
 
 
9e2500c
 
 
 
 
c5d7fde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e2500c
c5d7fde
9e2500c
 
 
 
 
 
 
c5d7fde
9e2500c
 
 
 
057c155
9e2500c
 
057c155
9e2500c
 
c5d7fde
9e2500c
 
c5d7fde
 
 
 
 
 
 
 
 
 
 
 
 
9e2500c
 
 
 
057c155
 
 
 
c5d7fde
 
057c155
 
c5d7fde
 
057c155
 
 
 
 
 
 
 
c5d7fde
057c155
 
 
 
 
c5d7fde
 
057c155
 
c5d7fde
057c155
 
 
c5d7fde
 
057c155
c5d7fde
057c155
c5d7fde
057c155
c5d7fde
057c155
c5d7fde
057c155
c5d7fde
 
057c155
c5d7fde
 
057c155
 
c5d7fde
057c155
c5d7fde
057c155
 
c5d7fde
057c155
 
c5d7fde
057c155
 
c5d7fde
057c155
 
 
c5d7fde
 
057c155
c5d7fde
057c155
 
 
c5d7fde
 
 
 
 
057c155
 
c5d7fde
 
057c155
c5d7fde
057c155
 
c5d7fde
057c155
9e2500c
c5d7fde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e2500c
 
c5d7fde
9e2500c
c5d7fde
 
 
 
 
 
 
9e2500c
 
 
c5d7fde
 
9e2500c
 
c5d7fde
 
9e2500c
 
c5d7fde
9e2500c
 
 
c5d7fde
9e2500c
c5d7fde
9e2500c
 
c5d7fde
9e2500c
 
 
 
 
 
 
c5d7fde
 
 
9e2500c
c5d7fde
 
 
9e2500c
 
 
c5d7fde
 
9e2500c
 
 
c5d7fde
9e2500c
 
 
c5d7fde
 
 
9e2500c
 
 
 
c5d7fde
 
 
 
9e2500c
 
c5d7fde
 
 
 
 
 
 
 
 
 
 
 
 
 
9e2500c
 
c5d7fde
 
 
 
9e2500c
c5d7fde
 
 
9e2500c
c5d7fde
 
9e2500c
c5d7fde
9e2500c
c5d7fde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e2500c
c5d7fde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9e2500c
c5d7fde
 
 
 
 
 
 
 
 
9e2500c
c5d7fde
9e2500c
c5d7fde
 
 
 
 
 
9e2500c
 
c5d7fde
9e2500c
057c155
9e2500c
c5d7fde
 
 
 
 
 
 
 
9e2500c
 
 
c5d7fde
9e2500c
 
c5d7fde
9e2500c
 
c5d7fde
9e2500c
c5d7fde
9e2500c
 
 
c5d7fde
9e2500c
c5d7fde
 
 
 
9e2500c
 
 
c5d7fde
 
9e2500c
c5d7fde
 
 
 
 
 
9e2500c
 
057c155
c5d7fde
 
 
057c155
c5d7fde
 
 
057c155
 
c5d7fde
 
 
057c155
c5d7fde
 
 
 
 
 
 
057c155
 
c5d7fde
 
 
057c155
c5d7fde
 
 
 
057c155
c5d7fde
057c155
c5d7fde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
# -*- coding: utf-8 -*-

# ==============================================================================
# PDF Form Filler & Template Generator with AI Enhancements
#
# คำอธิบาย:
#   - เครื่องมือนี้สร้างขึ้นด้วย Gradio สำหรับจัดการไฟล์ PDF และ CSV
#   - ความสามารถหลัก:
#     1. สร้าง Template (CSV/JSON) จากฟอร์ม PDF ที่มีอยู่
#     2. เติมข้อมูลจากไฟล์ CSV ลงในฟอร์ม PDF ทีละหลายๆ ไฟล์
#     3. หาก PDF ไม่มีฟอร์ม จะสร้าง PDF ใหม่จากข้อมูลในแต่ละแถวของ CSV
#     4. (ทางเลือก) ใช้ AI และ OCR เพื่อแปลงข้อมูลจากรูปภาพเป็น CSV
#     5. (ทางเลือก) ใช้ AI ช่วยแนะนำการจับคู่คอลัมน์ CSV กับช่องใน PDF
#     6. (ทางเลือก) ใช้ AI ช่วยตรวจสอบและทำความสะอาดข้อมูลก่อนสร้าง PDF
#
# การติดตั้ง Dependencies:
#   - pip install gradio pandas PyPDF2 reportlab
#   - สำหรับฟีเจอร์ AI/OCR (ทางเลือก):
#     - pip install Pillow numpy opencv-python pytesseract
#     - ต้องติดตั้ง Tesseract OCR Engine ในระบบของคุณและตั้งค่า PATH ให้ถูกต้อง
#     - https://github.com/tesseract-ocr/tesseract
#   - สำหรับฟีเจอร์ SambaNova AI (ทางเลือก):
#     - pip install 'gradio_client>=0.12.0'
#
# ==============================================================================

import gradio as gr
import pandas as pd
import json
import io
import zipfile
from datetime import datetime
import traceback
import tempfile
import os
import sys
import subprocess

# --- ตรวจสอบและติดตั้ง Dependencies ---
try:
    from PyPDF2 import PdfReader, PdfWriter
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import letter
    from reportlab.pdfbase import pdfmetrics
    from reportlab.pdfbase.ttfonts import TTFont
    print("Dependencies หลักถูกติดตั้งเรียบร้อยแล้ว")
except ImportError:
    print("กำลังติดตั้ง dependencies ที่จำเป็น: PyPDF2, reportlab, pandas")
    subprocess.check_call([sys.executable, "-m", "pip", "install", "PyPDF2", "reportlab", "pandas"])
    from PyPDF2 import PdfReader, PdfWriter
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import letter

# --- Dependencies เสริมสำหรับ AI และ OCR (จะแจ้งเตือนถ้าไม่มี) ---
try:
    from PIL import Image
    import numpy as np
    import cv2
    import pytesseract
    AI_OCR_ENABLED = True
    print("Dependencies สำหรับ AI/OCR พร้อมใช้งาน")
except ImportError:
    AI_OCR_ENABLED = False
    print("คำเตือน: ไม่พบ Dependencies สำหรับ AI/OCR (Pillow, numpy, opencv-python, pytesseract)")
    print("ฟังก์ชันที่เกี่ยวกับรูปภาพและ OCR จะไม่สามารถใช้งานได้")
    print("ติดตั้งด้วย: pip install Pillow numpy opencv-python pytesseract และติดตั้ง Tesseract engine")

try:
    from gradio_client import Client
    SAMBANOVA_AI_ENABLED = True
    print("Dependencies สำหรับ SambaNova AI พร้อมใช้งาน")
except ImportError:
    SAMBANOVA_AI_ENABLED = False
    print("คำเตือน: ไม่พบ Gradio Client (pip install 'gradio_client>=0.12.0')")
    print("ฟังก์ชันที่ต้องใช้ AI Model จะไม่สามารถใช้งานได้")


# ==============================================================================
# ส่วนของฟังก์ชันหลัก (Core Functions)
# ==============================================================================

def analyze_pdf_fields(pdf_path):
    """วิเคราะห์ฟิลด์ใน PDF และคืนค่าเป็น Dictionary"""
    try:
        reader = PdfReader(pdf_path)
        all_fields = {}
        # ตรวจสอบจาก AcroForm
        if reader.trailer.get("/Root") and reader.trailer["/Root"].get("/AcroForm"):
            acro_form = reader.trailer["/Root"]["/AcroForm"]
            if "/Fields" in acro_form:
                for field in acro_form["/Fields"]:
                    field_obj = field.get_object()
                    if "/T" in field_obj:
                        field_name = str(field_obj["/T"]).strip("()")
                        field_type = str(field_obj.get("/FT", "Unknown"))
                        field_value = str(field_obj.get("/V", "")).strip("()")
                        all_fields[field_name] = {
                            'type': field_type,
                            'default_value': field_value,
                            'method': 'AcroForm'
                        }
        # ตรวจสอบจาก Annotations ในแต่ละหน้า
        for page_num, page in enumerate(reader.pages):
            if "/Annots" in page:
                for annotation in page["/Annots"]:
                    annot_obj = annotation.get_object()
                    if annot_obj.get("/Subtype") == "/Widget" and "/T" in annot_obj:
                        field_name = str(annot_obj["/T"]).strip("()")
                        if field_name not in all_fields: # เพิ่มเฉพาะที่ยังไม่มี
                            field_type = str(annot_obj.get("/FT", "Widget"))
                            field_value = str(annot_obj.get("/V", "")).strip("()")
                            all_fields[field_name] = {
                                'type': field_type,
                                'default_value': field_value,
                                'page': page_num + 1,
                                'method': 'Annotation'
                            }
        return all_fields
    except Exception as e:
        return {"error": str(e)}

def generate_csv_template(pdf_fields, num_rows=5):
    """สร้าง CSV template จาก PDF fields"""
    if not pdf_fields or "error" in pdf_fields:
        return None, "ไม่สามารถสร้าง CSV template ได้"
    template_data = {'id': list(range(1, num_rows + 1))}
    for field_name in pdf_fields.keys():
        if field_name and field_name.strip():
            clean_name = field_name.strip()
            sample_value = f"ข้อมูลสำหรับ {clean_name} {{}}"
            template_data[clean_name] = [sample_value.format(i) for i in range(1, num_rows + 1)]
    df = pd.DataFrame(template_data)
    return df, "สร้าง CSV template สำเร็จ"

def generate_json_template(pdf_fields):
    """สร้าง JSON template จาก PDF fields"""
    if not pdf_fields or "error" in pdf_fields:
        return None, "ไม่สามารถสร้าง JSON template ได้"
    template = {
        "pdf_info": {"total_fields": len(pdf_fields), "generation_time": datetime.now().isoformat()},
        "fields": {},
        "sample_data": []
    }
    for field_name, field_info in pdf_fields.items():
        if field_name and field_name.strip():
            template["fields"][field_name.strip()] = field_info
    for i in range(1, 4):
        sample_record = {"id": i}
        for field_name in template["fields"].keys():
            sample_record[field_name] = f"ข้อมูลตัวอย่าง {i}"
        template["sample_data"].append(sample_record)
    return template, "สร้าง JSON template สำเร็จ"

def create_template_files(pdf_file, num_rows, progress=gr.Progress()):
    """สร้างไฟล์ template (CSV, JSON, README) และรวมเป็น ZIP"""
    if pdf_file is None:
        return None, "❌ กรุณาอัพโหลดไฟล์ PDF ก่อน"
    
    progress(0, desc="กำลังวิเคราะห์ PDF...")
    try:
        pdf_fields = analyze_pdf_fields(pdf_file.name)
        if not pdf_fields or "error" in pdf_fields:
            return None, "❌ ไม่พบ Form Fields ใน PDF หรือไฟล์เสียหาย"
        
        progress(0.3, desc="กำลังสร้าง CSV template...")
        csv_df, _ = generate_csv_template(pdf_fields, num_rows)
        
        progress(0.6, desc="กำลังสร้าง JSON template...")
        json_template, _ = generate_json_template(pdf_fields)
        
        if csv_df is None or json_template is None:
            return None, "❌ ไม่สามารถสร้างไฟล์ template ได้"
        
        progress(0.8, desc="กำลังบีบอัดไฟล์เป็น ZIP...")
        zip_buffer = io.BytesIO()
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_f:
            csv_buffer = io.StringIO()
            csv_df.to_csv(csv_buffer, index=False, encoding='utf-8-sig')
            zip_f.writestr(f"template_{timestamp}.csv", csv_buffer.getvalue())
            
            json_str = json.dumps(json_template, ensure_ascii=False, indent=2)
            zip_f.writestr(f"template_{timestamp}.json", json_str)
            
            readme_content = f"""# PDF Form Template Files
Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
PDF Fields Found: {len(pdf_fields)}
{chr(10).join([f"- {name}" for name in pdf_fields.keys()])}
"""
            zip_f.writestr("README.txt", readme_content)
        
        zip_buffer.seek(0)
        
        # บันทึกไฟล์ ZIP ชั่วคราวเพื่อให้ Gradio ส่งให้ผู้ใช้ได้
        temp_dir = tempfile.gettempdir()
        zip_filename = f"pdf_templates_{timestamp}.zip"
        temp_zip_path = os.path.join(temp_dir, zip_filename)
        with open(temp_zip_path, "wb") as f:
            f.write(zip_buffer.getvalue())
        
        progress(1, desc="สร้างไฟล์สำเร็จ!")
        result_msg = f"✅ สร้าง template สำเร็จ!\n- พบ {len(pdf_fields)} fields\n- CSV มี {num_rows} แถวตัวอย่าง"
        
        return temp_zip_path, result_msg
        
    except Exception as e:
        return None, f"❌ เกิดข้อผิดพลาด: {e}\n{traceback.format_exc()}"

def fill_pdf_form(pdf_path, field_data):
    """เติมข้อมูลลงในฟอร์มของ PDF"""
    reader = PdfReader(pdf_path)
    writer = PdfWriter()
    writer.append_pages_from_reader(reader)
    
    # เติมข้อมูลในฟอร์ม
    for page in writer.pages:
        try:
            writer.update_page_form_field_values(page, field_data, auto_regenerate=False)
        except Exception:
            # บางครั้ง field อยูในระดับ root
            pass
    try: # ลองเติมที่ root อีกครั้ง
        writer.update_page_form_field_values(writer.pages[0], field_data)
    except:
        pass

    output_buffer = io.BytesIO()
    writer.write(output_buffer)
    output_buffer.seek(0)
    return output_buffer.getvalue()

def create_simple_pdf(data_row, filename):
    """สร้าง PDF ใหม่แบบง่ายๆ กรณีที่ PDF ต้นฉบับไม่มีฟอร์ม"""
    buffer = io.BytesIO()
    # ใช้ font ที่รองรับภาษาไทย
    try:
        pdfmetrics.registerFont(TTFont('THSarabunNew', 'THSarabunNew.ttf'))
        font_name = 'THSarabunNew'
    except:
        font_name = 'Helvetica' # Fallback

    p = canvas.Canvas(buffer, pagesize=letter)
    width, height = letter
    
    p.setFont(font_name, 16)
    p.drawString(50, height - 50, f"เอกสาร: {filename.replace('.pdf', '')}")
    p.line(50, height - 60, 550, height - 60)
    
    y_position = height - 90
    p.setFont(font_name, 12)
    for column, value in data_row.items():
        if pd.notna(value) and str(value).strip():
            text = f"{str(column).strip()}: {str(value).strip()}"
            try:
                p.drawString(50, y_position, text)
            except:
                safe_text = text.encode('latin-1', 'replace').decode('latin-1')
                p.drawString(50, y_position, safe_text)
            y_position -= 20
            if y_position < 50:
                p.showPage()
                p.setFont(font_name, 12)
                y_position = height - 50
    
    p.save()
    buffer.seek(0)
    return buffer.getvalue()

def read_csv_safe(csv_file):
    """อ่านไฟล์ CSV โดยลองหลาย encoding และ separator เพื่อความยืดหยุ่น"""
    encodings = ['utf-8-sig', 'utf-8', 'cp874', 'tis-620']
    separators = [',', ';', '\t']
    
    # ใช้ .name เพราะ Gradio ส่งมาเป็น object ที่มี path อยู่ใน .name
    filepath = csv_file.name 

    for encoding in encodings:
        for sep in separators:
            try:
                df = pd.read_csv(filepath, encoding=encoding, sep=sep, engine='python')
                if len(df.columns) > 1:
                    return df, None
            except Exception:
                continue
    return None, "ไม่สามารถอ่านไฟล์ CSV ได้ ลองตรวจสอบ Encoding (ควรเป็น UTF-8) และ Separator (ควรเป็น ,)"

def process_pdf_csv(pdf_file, csv_file, filename_column, file_prefix, use_form_fields, progress=gr.Progress()):
    """ฟังก์ชันหลักสำหรับประมวลผล PDF และ CSV"""
    if not pdf_file or not csv_file:
        return None, "❌ กรุณาอัพโหลดทั้งไฟล์ PDF และ CSV"

    try:
        df, csv_error = read_csv_safe(csv_file)
        if df is None:
            return None, f"❌ ไม่สามารถอ่าน CSV ได้: {csv_error}"

        pdf_path = pdf_file.name
        pdf_fields = analyze_pdf_fields(pdf_path)
        has_form_fields = bool(pdf_fields and "error" not in pdf_fields)
        
        generated_pdfs = {}
        log = []
        total_rows = len(df)

        for index, row in df.iterrows():
            progress((index + 1) / total_rows, f"ประมวลผลแถวที่ {index + 1}/{total_rows}")
            
            # สร้างชื่อไฟล์
            if filename_column and filename_column in df.columns and pd.notna(row[filename_column]):
                safe_name = "".join(c for c in str(row[filename_column]) if c.isalnum() or c in (' ', '-', '_')).strip()
                filename = f"{file_prefix}_{safe_name}.pdf"
            else:
                filename = f"{file_prefix}_{index + 1:03d}.pdf"
            
            row_data = row.to_dict()
            
            try:
                if use_form_fields and has_form_fields:
                    # เติมฟอร์ม PDF ที่มีอยู่
                    pdf_content = fill_pdf_form(pdf_path, row_data)
                    status = "เติมฟอร์มสำเร็จ"
                else:
                    # สร้าง PDF ใหม่
                    pdf_content = create_simple_pdf(row_data, filename)
                    status = "สร้าง PDF ใหม่" if not has_form_fields else "สร้าง PDF ใหม่ (Fallback)"
                
                generated_pdfs[filename] = pdf_content
                log.append(f"✅ {filename}: {status}")
            except Exception as e:
                log.append(f"❌ {filename}: เกิดข้อผิดพลาด - {e}")
        
        if not generated_pdfs:
            return None, "❌ ไม่สามารถสร้าง PDF ได้เลย\n" + "\n".join(log)

        # สร้างไฟล์ ZIP
        zip_buffer = io.BytesIO()
        with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_f:
            for filename, pdf_content in generated_pdfs.items():
                zip_f.writestr(filename, pdf_content)
            zip_f.writestr("processing_log.txt", "\n".join(log))
        zip_buffer.seek(0)

        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        zip_filename = f"generated_pdfs_{timestamp}.zip"
        temp_zip_path = os.path.join(tempfile.gettempdir(), zip_filename)
        with open(temp_zip_path, 'wb') as f:
            f.write(zip_buffer.getvalue())
            
        result_message = f"✅ สร้าง PDF สำเร็จ {len(generated_pdfs)} ไฟล์!\nดูรายละเอียดใน processing_log.txt"
        return temp_zip_path, result_message

    except Exception as e:
        return None, f"❌ เกิดข้อผิดพลาดร้ายแรง: {e}\n{traceback.format_exc()}"


# ==============================================================================
# ส่วนของฟังก์ชัน AI และ OCR (ทางเลือก)
# ==============================================================================

def init_sambanova_ai():
    """Initialize SambaNova AI model client."""
    if not SAMBANOVA_AI_ENABLED:
        print("SambaNova AI is disabled.")
        return None
    try:
        # ใช้ gradio_client.Client แทน gr.load ที่อาจมีปัญหา
        client = Client("sambanova/Llama-3-8B-Instruct", hf_token="YOUR_HF_TOKEN") # ใส่ Hugging Face Token ของคุณ
        print("SambaNova AI client initialized successfully.")
        return client
    except Exception as e:
        print(f"Error initializing SambaNova AI: {e}")
        return None

def extract_text_from_image(image_file):
    """Extract text from an image file using Tesseract OCR."""
    if not AI_OCR_ENABLED or image_file is None:
        return "", "OCR is not available or no image provided."
    try:
        image = Image.open(image_file.name)
        # ตั้งค่า Tesseract ให้ตรวจจับทั้งภาษาไทยและอังกฤษ
        custom_config = r'--oem 3 --psm 6 -l tha+eng'
        text = pytesseract.image_to_string(image, config=custom_config)
        return text.strip(), "Text extracted successfully."
    except Exception as e:
        return "", f"OCR Error: {e}. ตรวจสอบว่าติดตั้ง Tesseract Engine ถูกต้อง"

def image_to_csv_with_ai(image_file, progress=gr.Progress()):
    """Convert data from an image to a CSV file using OCR and AI for structuring."""
    if not AI_OCR_ENABLED:
        return None, "❌ ฟังก์ชันนี้ต้องการ AI/OCR dependencies"
    if image_file is None:
        return None, "❌ กรุณาอัพโหลดรูปภาพ"
        
    progress(0.2, desc="กำลังอ่านข้อความจากรูปภาพ (OCR)...")
    raw_text, ocr_status = extract_text_from_image(image_file)
    if not raw_text:
        return None, f"❌ ไม่พบข้อความในรูปภาพ: {ocr_status}"

    progress(0.5, desc="กำลังใช้ AI จัดโครงสร้างข้อมูล...")
    ai_client = init_sambanova_ai()
    if not ai_client:
        return None, "❌ ไม่สามารถเชื่อมต่อ AI Model ได้"

    prompt = f"""
From the following text, extract key-value pairs. The output should be only the data in 'key: value' format, one per line.
Example:
Name: John Doe
Address: 123 Main St
Date: 2024-01-15

Text to process:
---
{raw_text}
---
"""
    try:
        # การเรียกใช้งาน API ของ gradio_client
        result = ai_client.predict(message=prompt, api_name="/chat")
        
        progress(0.8, desc="กำลังสร้างไฟล์ CSV...")
        lines = result.strip().split('\n')
        data = [line.split(':', 1) for line in lines if ':' in line]
        
        if not data:
            return None, "AI ไม่สามารถจัดโครงสร้างข้อมูลได้"
            
        df = pd.DataFrame(data, columns=['Field', 'Value']).set_index('Field').T
        
        csv_buffer = io.StringIO()
        df.to_csv(csv_buffer, index=False, encoding='utf-8-sig')
        
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        temp_csv_path = os.path.join(tempfile.gettempdir(), f"extracted_data_{timestamp}.csv")
        with open(temp_csv_path, 'w', encoding='utf-8-sig') as f:
            f.write(csv_buffer.getvalue())
            
        return temp_csv_path, "✅ แปลงรูปภาพเป็น CSV สำเร็จ"
    except Exception as e:
        return None, f"❌ เกิดข้อผิดพลาดระหว่างประมวลผลด้วย AI: {e}"


# ==============================================================================
# ส่วนของ UI Analysis Functions
# ==============================================================================

def analyze_pdf_info(pdf_file):
    """วิเคราะห์และแสดงข้อมูลสรุปของไฟล์ PDF บน UI"""
    if pdf_file is None:
        return "ยังไม่มีไฟล์ PDF"
    try:
        reader = PdfReader(pdf_file.name)
        info = f"📄 **ข้อมูล PDF:**\n- จำนวนหน้า: {len(reader.pages)}\n"
        pdf_fields = analyze_pdf_fields(pdf_file.name)
        if pdf_fields and "error" not in pdf_fields:
            info += f"- **พบ Form Fields: {len(pdf_fields)} ช่อง** (จะใช้วิธีเติมฟอร์ม)\n"
            info += "\n🏷️ **ตัวอย่างชื่อ Fields:**\n"
            for name in list(pdf_fields.keys())[:10]:
                info += f"  - `{name}`\n"
            if len(pdf_fields) > 10:
                info += f"  - ... และอีก {len(pdf_fields) - 10} fields\n"
        else:
            info += "- **ไม่พบ Form Fields** (จะใช้วิธีสร้าง PDF ใหม่ทับลงบนกระดาษเปล่า)\n"
        return info
    except Exception as e:
        return f"❌ ไม่สามารถวิเคราะห์ PDF: {e}"

def analyze_csv_info(csv_file):
    """วิเคราะห์และแสดงข้อมูลสรุปของไฟล์ CSV และอัปเดต Dropdown"""
    if csv_file is None:
        return "ยังไม่มีไฟล์ CSV", gr.update(choices=[], value=None)
    try:
        df, error = read_csv_safe(csv_file)
        if df is None:
            return f"❌ ไม่สามารถอ่าน CSV: {error}", gr.update(choices=[], value=None)
        
        info = f"📋 **ข้อมูล CSV:**\n- จำนวนแถว: {len(df)}\n- จำนวนคอลัมน์: {len(df.columns)}\n"
        info += "\n📝 **รายชื่อคอลัมน์:**\n"
        for col in df.columns[:15]:
            info += f"  - `{col}`\n"
        if len(df.columns) > 15:
            info += f"  - ... และอีก {len(df.columns) - 15} คอลัมน์\n"
        
        # อัปเดต Dropdown สำหรับเลือกคอลัมน์ชื่อไฟล์
        return info, gr.update(choices=df.columns.tolist(), value=None)
    except Exception as e:
        return f"❌ ไม่สามารถวิเคราะห์ CSV: {e}", gr.update(choices=[], value=None)


# ==============================================================================
# ส่วนของการสร้าง Gradio Interface
# ==============================================================================

def create_interface():
    with gr.Blocks(title="PDF Form Filler & Template Generator", theme=gr.themes.Soft()) as app:
        gr.Markdown("# 📄 เครื่องมือจัดการ PDF จากข้อมูล CSV")
        gr.Markdown("รองรับการ **สร้าง Template** จาก PDF, **เติมข้อมูล** จาก CSV, และ **แปลงรูปภาพเป็น CSV** ด้วย AI")

        with gr.Tabs():
            # --- Tab 1: สร้าง Template ---
            with gr.TabItem("🔄 1. สร้าง Template"):
                gr.Markdown("## สร้าง CSV/JSON Template จาก PDF ที่มี Form Fields")
                with gr.Row():
                    with gr.Column(scale=1):
                        template_pdf = gr.File(label="📄 อัพโหลด PDF ต้นฉบับ", file_types=[".pdf"])
                        num_sample_rows = gr.Slider(label="จำนวนแถวตัวอย่างใน CSV", minimum=1, maximum=50, value=5, step=1)
                        generate_template_btn = gr.Button("🚀 สร้าง Template", variant="primary")
                    with gr.Column(scale=2):
                        template_pdf_info = gr.Markdown("อัพโหลด PDF เพื่อดูข้อมูล...")
                        template_result_file = gr.File(label="📦 ดาวน์โหลดไฟล์ Template (ZIP)", interactive=False)
                        template_result_message = gr.Markdown()

            # --- Tab 2: เติมข้อมูล PDF ---
            with gr.TabItem("📝 2. เติมข้อมูล PDF"):
                gr.Markdown("## เติมข้อมูลลงใน PDF จากไฟล์ CSV")
                with gr.Row():
                    with gr.Column(scale=1):
                        gr.Markdown("### 📂 1. อัพโหลดไฟล์")
                        pdf_file = gr.File(label="📄 PDF Form ต้นฉบับ", file_types=[".pdf"])
                        csv_file = gr.File(label="📊 CSV ข้อมูล", file_types=[".csv"])
                        
                        gr.Markdown("### ⚙️ 2. ตั้งค่า")
                        use_form_fields = gr.Checkbox(label="พยายามเติมข้อมูลลงใน Form Fields ที่มีอยู่", value=True)
                        file_prefix = gr.Textbox(label="คำนำหน้าชื่อไฟล์ (Prefix)", value="Document")
                        filename_column = gr.Dropdown(label="เลือกคอลัมน์ที่จะใช้เป็นชื่อไฟล์ (ถ้ามี)", interactive=True)
                        
                        fill_form_btn = gr.Button("🚀 เริ่มเติมข้อมูล", variant="primary")
                        
                    with gr.Column(scale=2):
                        pdf_info = gr.Markdown("อัพโหลด PDF เพื่อดูข้อมูล...")
                        csv_info = gr.Markdown("อัพโหลด CSV เพื่อดูข้อมูล...")
                        gr.Markdown("---")
                        filled_result_file = gr.File(label="📦 ดาวน์โหลด PDF ทั้งหมด (ZIP)", interactive=False)
                        filled_result_message = gr.Markdown()

            # --- Tab 3: Image to CSV (AI) ---
            with gr.TabItem("🖼️ 3. แปลงรูปภาพเป็น CSV (AI)"):
                gr.Markdown("## ใช้ OCR และ AI เพื่อดึงข้อมูลจากรูปภาพและสร้างเป็นไฟล์ CSV")
                with gr.Row():
                    with gr.Column(scale=1):
                        image_upload = gr.File(label="🖼️ อัพโหลดรูปภาพ (บิล, เอกสาร, ฯลฯ)", file_types=["image"])
                        image_to_csv_btn = gr.Button("🤖 แปลงเป็น CSV", variant="primary", visible=AI_OCR_ENABLED)
                        if not AI_OCR_ENABLED:
                            gr.Markdown("⚠️ *ฟังก์ชันนี้ถูกปิดใช้งานเนื่องจากไม่พบ Library ที่จำเป็น (Pillow, OpenCV, Pytesseract)*")

                    with gr.Column(scale=2):
                        image_csv_output = gr.File(label="📄 ดาวน์โหลดไฟล์ CSV ที่ได้", interactive=False)
                        image_csv_message = gr.Markdown()

        # --- Event Handlers ---
        template_pdf.change(fn=analyze_pdf_info, inputs=template_pdf, outputs=template_pdf_info)
        generate_template_btn.click(
            fn=create_template_files,
            inputs=[template_pdf, num_sample_rows],
            outputs=[template_result_file, template_result_message]
        )

        pdf_file.change(fn=analyze_pdf_info, inputs=pdf_file, outputs=pdf_info)
        csv_file.change(fn=analyze_csv_info, inputs=csv_file, outputs=[csv_info, filename_column])
        
        fill_form_btn.click(
            fn=process_pdf_csv,
            inputs=[pdf_file, csv_file, filename_column, file_prefix, use_form_fields],
            outputs=[filled_result_file, filled_result_message]
        )

        if AI_OCR_ENABLED:
            image_to_csv_btn.click(
                fn=image_to_csv_with_ai,
                inputs=[image_upload],
                outputs=[image_csv_output, image_csv_message]
            )
            
    return app

# --- Launch the application ---
if __name__ == "__main__":
    # ลองหา font ไทย ถ้าไม่มีจะได้ไม่ error ตอนสร้าง PDF
    try:
        from reportlab.pdfbase import pdfmetrics
        from reportlab.pdfbase.ttfonts import TTFont
        # สำหรับ Windows
        pdfmetrics.registerFont(TTFont('THSarabunNew', 'C:/Windows/Fonts/THSARI.TTF'))
        print("ลงทะเบียน Font 'THSarabunNew' สำหรับ ReportLab สำเร็จ")
    except:
        print("คำเตือน: ไม่พบ Font 'THSarabunNew' ในระบบ อาจทำให้การสร้าง PDF ภาษาไทยมีปัญหา")
        print("แนะนำให้ติดตั้งฟอนต์ TH SarabunPSK หรือปรับแก้ path ของฟอนต์ในโค้ด")

    app = create_interface()
    app.launch(debug=True)