PISAN commited on
Commit
c5d7fde
·
verified ·
1 Parent(s): 057c155

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +384 -441
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import gradio as gr
2
  import pandas as pd
3
  import json
@@ -7,35 +8,62 @@ from datetime import datetime
7
  import traceback
8
  import tempfile
9
  import os
 
 
10
 
11
- # ติดตั้ง dependencies ที่จำเป็น
12
  try:
13
  from PyPDF2 import PdfReader, PdfWriter
14
  from reportlab.pdfgen import canvas
15
  from reportlab.lib.pagesizes import letter
16
  from reportlab.pdfbase import pdfmetrics
17
  from reportlab.pdfbase.ttfonts import TTFont
18
- except ImportError as e:
19
- print(f"กำลังติดตั้ง dependencies: {e}")
20
- import subprocess
21
- import sys
22
  subprocess.check_call([sys.executable, "-m", "pip", "install", "PyPDF2", "reportlab", "pandas"])
23
  from PyPDF2 import PdfReader, PdfWriter
24
  from reportlab.pdfgen import canvas
25
  from reportlab.lib.pagesizes import letter
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  def analyze_pdf_fields(pdf_path):
28
- """วิเคราะห์ฟิลด์ใน PDF"""
29
  try:
30
  reader = PdfReader(pdf_path)
31
  all_fields = {}
32
-
33
  # ตรวจสอบจาก AcroForm
34
  if reader.trailer.get("/Root") and reader.trailer["/Root"].get("/AcroForm"):
35
  acro_form = reader.trailer["/Root"]["/AcroForm"]
36
  if "/Fields" in acro_form:
37
- fields = acro_form["/Fields"]
38
- for field in fields:
39
  field_obj = field.get_object()
40
  if "/T" in field_obj:
41
  field_name = str(field_obj["/T"]).strip("()")
@@ -46,28 +74,22 @@ def analyze_pdf_fields(pdf_path):
46
  'default_value': field_value,
47
  'method': 'AcroForm'
48
  }
49
-
50
- # ตรวจสอบจาก Annotations
51
  for page_num, page in enumerate(reader.pages):
52
  if "/Annots" in page:
53
- try:
54
- annotations = page["/Annots"]
55
- for annotation in annotations:
56
- annot_obj = annotation.get_object()
57
- if annot_obj.get("/Subtype") == "/Widget":
58
- if "/T" in annot_obj:
59
- field_name = str(annot_obj["/T"]).strip("()")
60
- field_type = str(annot_obj.get("/FT", "Widget"))
61
- field_value = str(annot_obj.get("/V", "")).strip("()")
62
- all_fields[field_name] = {
63
- 'type': field_type,
64
- 'default_value': field_value,
65
- 'page': page_num + 1,
66
- 'method': 'Annotation'
67
- }
68
- except Exception:
69
- continue
70
-
71
  return all_fields
72
  except Exception as e:
73
  return {"error": str(e)}
@@ -76,42 +98,12 @@ def generate_csv_template(pdf_fields, num_rows=5):
76
  """สร้าง CSV template จาก PDF fields"""
77
  if not pdf_fields or "error" in pdf_fields:
78
  return None, "ไม่สามารถสร้าง CSV template ได้"
79
-
80
- # สร้าง DataFrame ว่าง
81
- template_data = {}
82
-
83
- # เพิ่มคอลัมน์ ID
84
- template_data['id'] = list(range(1, num_rows + 1))
85
-
86
- # เพิ่มคอลัมน์จาก PDF fields
87
- for field_name, field_info in pdf_fields.items():
88
  if field_name and field_name.strip():
89
  clean_name = field_name.strip()
90
- # ใส่ค่าตัวอย่าง
91
- if field_info.get('default_value') and field_info['default_value'].strip():
92
- sample_value = field_info['default_value']
93
- else:
94
- # สร้างค่าตัวอย่าง
95
- if 'name' in clean_name.lower():
96
- sample_value = f"ชื่อตัวอย่าง {{}}"
97
- elif 'date' in clean_name.lower() or 'วันที่' in clean_name:
98
- sample_value = "2024-01-01"
99
- elif 'email' in clean_name.lower():
100
- sample_value = "example{}@email.com"
101
- elif 'phone' in clean_name.lower() or 'เบอร์' in clean_name:
102
- sample_value = "08-1234-567{}"
103
- elif 'address' in clean_name.lower() or 'ที่อยู่' in clean_name:
104
- sample_value = "123 ถนนตัวอย่าง กรุงเทพ {}"
105
- else:
106
- sample_value = f"ข้อมูลตัวอย่าง {{}}"
107
-
108
- # สร้างข้อมูลตัวอย่าง
109
- template_data[clean_name] = [
110
- sample_value.format(i) if '{}' in sample_value else sample_value
111
- for i in range(1, num_rows + 1)
112
- ]
113
-
114
- # สร้าง DataFrame
115
  df = pd.DataFrame(template_data)
116
  return df, "สร้าง CSV template สำเร็จ"
117
 
@@ -119,503 +111,454 @@ def generate_json_template(pdf_fields):
119
  """สร้าง JSON template จาก PDF fields"""
120
  if not pdf_fields or "error" in pdf_fields:
121
  return None, "ไม่สามารถสร้าง JSON template ได้"
122
-
123
  template = {
124
- "pdf_info": {
125
- "total_fields": len(pdf_fields),
126
- "generation_time": datetime.now().isoformat()
127
- },
128
  "fields": {},
129
  "sample_data": []
130
  }
131
-
132
- # เพิ่มข้อมูล fields
133
  for field_name, field_info in pdf_fields.items():
134
  if field_name and field_name.strip():
135
- clean_name = field_name.strip()
136
- template["fields"][clean_name] = {
137
- "type": field_info.get('type', 'Unknown'),
138
- "default_value": field_info.get('default_value', ''),
139
- "page": field_info.get('page', 1),
140
- "method": field_info.get('method', 'Unknown')
141
- }
142
-
143
- # สร้างข้อมูลตัวอย่าง
144
- for i in range(1, 4): # 3 ตัวอย่าง
145
  sample_record = {"id": i}
146
  for field_name in template["fields"].keys():
147
- if 'name' in field_name.lower():
148
- sample_record[field_name] = f"ชื่อตัวอย่าง {i}"
149
- elif 'date' in field_name.lower() or 'วันที่' in field_name:
150
- sample_record[field_name] = f"2024-0{i}-01"
151
- elif 'email' in field_name.lower():
152
- sample_record[field_name] = f"example{i}@email.com"
153
- else:
154
- sample_record[field_name] = f"ข้อมูลตัวอย่าง {i}"
155
  template["sample_data"].append(sample_record)
156
-
157
  return template, "สร้าง JSON template สำเร็จ"
158
 
159
- def create_template_files(pdf_file, num_rows=5):
160
- """สร้างไฟล์ template ทั้งหมด"""
161
  if pdf_file is None:
162
- return None, None, "❌ กรุณาอัพโหลดไฟล์ PDF"
163
 
 
164
  try:
165
- # วิเคราะห์ PDF
166
- pdf_fields = analyze_pdf_fields(pdf_file)
167
-
168
  if not pdf_fields or "error" in pdf_fields:
169
- return None, None, "❌ ไม่พบ form fields ใน PDF หรือไม่สามารถอ่านได้"
170
 
171
- # สร้าง CSV template
172
- csv_df, csv_msg = generate_csv_template(pdf_fields, num_rows)
173
 
174
- # สร้าง JSON template
175
- json_template, json_msg = generate_json_template(pdf_fields)
176
 
177
  if csv_df is None or json_template is None:
178
- return None, None, "❌ ไม่สามารถสร้าง template ได้"
179
 
180
- # สร้าง ZIP file
181
  zip_buffer = io.BytesIO()
182
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
183
-
184
- with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
185
- # เพิ่ม CSV
186
  csv_buffer = io.StringIO()
187
  csv_df.to_csv(csv_buffer, index=False, encoding='utf-8-sig')
188
- zip_file.writestr(f"template_{timestamp}.csv", csv_buffer.getvalue())
189
 
190
- # เพิ่ม JSON
191
  json_str = json.dumps(json_template, ensure_ascii=False, indent=2)
192
- zip_file.writestr(f"template_{timestamp}.json", json_str)
193
 
194
- # เพิ่ม README
195
  readme_content = f"""# PDF Form Template Files
196
  Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
197
-
198
- ## Files included:
199
- 1. template_{timestamp}.csv - CSV template with sample data
200
- 2. template_{timestamp}.json - JSON template with field information
201
-
202
- ## PDF Fields Found: {len(pdf_fields)}
203
- {chr(10).join([f"- {name}: {info.get('type', 'Unknown')}" for name, info in pdf_fields.items()])}
204
-
205
- ## How to use:
206
- 1. Edit the CSV file with your actual data
207
- 2. Use the CSV file with the original PDF form filler
208
- 3. The JSON file contains detailed field information for reference
209
-
210
- ## Note:
211
- - Make sure column names in CSV match the field names in PDF
212
- - You can add more rows to the CSV as needed
213
- - Keep the column headers exactly as shown
214
  """
215
- zip_file.writestr("README.txt", readme_content)
216
 
217
  zip_buffer.seek(0)
218
 
219
- # บันทึกไฟล์ชั่วคราว
220
- temp_zip_path = os.path.join(tempfile.gettempdir(), f"pdf_templates_{timestamp}.zip")
221
- with open(temp_zip_path, 'wb') as f:
 
 
222
  f.write(zip_buffer.getvalue())
223
 
224
- result_msg = f"✅ สร้าง template สำเร็จ!\n"
225
- result_msg += f"🔍 พบ {len(pdf_fields)} fields ใน PDF\n"
226
- result_msg += f"📄 CSV template: {num_rows} แถวตัวอย่าง\n"
227
- result_msg += f"📋 JSON template: รายละเอียดครบถ้วน\n"
228
- result_msg += f"📁 README: คำแนะนำการใช้งาน"
229
 
230
- return temp_zip_path, pdf_fields, result_msg
231
 
232
  except Exception as e:
233
- return None, None, f"❌ เกิดข้อผิดพลาด: {str(e)}"
234
 
235
  def fill_pdf_form(pdf_path, field_data):
236
- """เติมข้อมูลในฟอร์ม PDF"""
237
- try:
238
- reader = PdfReader(pdf_path)
239
- writer = PdfWriter()
240
-
241
- # คัดลอกหน้าทั้งหมด
242
- for page in reader.pages:
243
- writer.add_page(page)
244
-
245
- # เติมข้อมูลในฟอร์ม
246
- if hasattr(writer, 'update_page_form_field_values'):
247
- for page_num, page in enumerate(writer.pages):
248
- try:
249
- writer.update_page_form_field_values(page, field_data)
250
- except Exception:
251
- pass
252
-
253
- # ลองวิธีอื่น
254
- elif "/AcroForm" in reader.trailer.get("/Root", {}):
255
- try:
256
- acro_form = reader.trailer["/Root"]["/AcroForm"]
257
- if "/Fields" in acro_form:
258
- fields = acro_form["/Fields"]
259
- for field in fields:
260
- field_obj = field.get_object()
261
- if "/T" in field_obj:
262
- field_name = str(field_obj["/T"]).strip("()")
263
- if field_name in field_data:
264
- try:
265
- field_obj.update({"/V": field_data[field_name]})
266
- except Exception:
267
- pass
268
- except Exception:
269
- pass
270
-
271
- return writer
272
- except Exception as e:
273
- raise Exception(f"ไม่สามารถเติมฟอร์มได้: {str(e)}")
274
 
275
  def create_simple_pdf(data_row, filename):
276
- """สร้าง PDF ใหม่แบบง่าย"""
277
  buffer = io.BytesIO()
 
 
 
 
 
 
 
278
  p = canvas.Canvas(buffer, pagesize=letter)
279
  width, height = letter
280
 
281
- # ตั้งค่า font
282
- p.setFont("Helvetica", 12)
283
-
284
- # หัวเรื่อง
285
- p.setFont("Helvetica-Bold", 16)
286
- title = f"Document: {filename.replace('.pdf', '')}"
287
- p.drawString(50, height - 50, title)
288
  p.line(50, height - 60, 550, height - 60)
289
 
290
- # เนื้อหา
291
- y_position = height - 100
292
- p.setFont("Helvetica", 12)
293
-
294
  for column, value in data_row.items():
295
  if pd.notna(value) and str(value).strip():
296
- clean_column = str(column).strip()
297
- clean_value = str(value).strip()
298
-
299
- if len(clean_value) > 80:
300
- clean_value = clean_value[:77] + "..."
301
-
302
- text = f"{clean_column}: {clean_value}"
303
-
304
  try:
305
  p.drawString(50, y_position, text)
306
  except:
307
- safe_text = text.encode('ascii', errors='ignore').decode('ascii')
308
  p.drawString(50, y_position, safe_text)
309
-
310
- y_position -= 25
311
-
312
  if y_position < 50:
313
  p.showPage()
314
- p.setFont("Helvetica", 12)
315
  y_position = height - 50
316
 
317
- # เวลาที่สร้าง
318
- p.setFont("Helvetica", 8)
319
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
320
- p.drawString(50, 30, f"Created: {timestamp}")
321
-
322
  p.save()
323
  buffer.seek(0)
324
  return buffer.getvalue()
325
 
326
- def process_single_row(pdf_path, row_data, filename, use_form=True):
327
- """ประมวลผลแถวเดียว"""
328
- try:
329
- # เตรียมข้อมูลฟิลด์
330
- field_data = {}
331
- for column, value in row_data.items():
332
- if pd.notna(value) and str(value).strip():
333
- clean_value = str(value).strip()
334
- clean_column = str(column).strip()
335
-
336
- # ลองหลายรูปแบบของชื่อฟิลด์
337
- field_variations = [
338
- clean_column,
339
- clean_column.lower(),
340
- clean_column.upper(),
341
- clean_column.replace('_', ' '),
342
- clean_column.replace(' ', '_'),
343
- clean_column.replace('-', '_'),
344
- clean_column.replace('_', '')
345
- ]
346
-
347
- for variation in field_variations:
348
- field_data[variation] = clean_value
349
-
350
- if use_form:
351
- try:
352
- # ลองเติมฟอร์ม
353
- writer = fill_pdf_form(pdf_path, field_data)
354
-
355
- output_buffer = io.BytesIO()
356
- writer.write(output_buffer)
357
- output_buffer.seek(0)
358
- return output_buffer.getvalue(), "form_filled"
359
- except Exception as e:
360
- # ถ้าไม่ได้ ให้สร้างใหม่
361
- pdf_content = create_simple_pdf(row_data, filename)
362
- return pdf_content, f"new_pdf_created: {str(e)}"
363
- else:
364
- # สร้าง PDF ใหม่
365
- pdf_content = create_simple_pdf(row_data, filename)
366
- return pdf_content, "new_pdf_created"
367
-
368
- except Exception as e:
369
- return None, f"error: {str(e)}"
370
-
371
  def read_csv_safe(csv_file):
372
- """อ่าน CSV อย่างปลอดภัย"""
373
- encodings = ['utf-8', 'utf-8-sig', 'cp874', 'tis-620', 'iso-8859-1', 'cp1252']
374
- separators = [',', ';', '\t', '|']
375
 
 
 
 
376
  for encoding in encodings:
377
  for sep in separators:
378
  try:
379
- df = pd.read_csv(csv_file, encoding=encoding, sep=sep, engine='python')
380
- if len(df.columns) > 1 and len(df) > 0:
381
  return df, None
382
  except Exception:
383
  continue
384
-
385
- try:
386
- df = pd.read_csv(csv_file)
387
- return df, None
388
- except Exception as e:
389
- return None, str(e)
390
 
391
  def process_pdf_csv(pdf_file, csv_file, filename_column, file_prefix, use_form_fields, progress=gr.Progress()):
392
  """ฟังก์ชันหลักสำหรับประมวลผล PDF และ CSV"""
393
-
394
- if pdf_file is None or csv_file is None:
395
- return None, "❌ กรุณาอัพโหลดไฟล์ PDF และ CSV"
396
-
397
  try:
398
- # อ่าน CSV
399
  df, csv_error = read_csv_safe(csv_file)
400
  if df is None:
401
  return None, f"❌ ไม่สามารถอ่าน CSV ได้: {csv_error}"
 
 
 
 
402
 
403
- # วิเคราะห์ PDF
404
- pdf_fields = analyze_pdf_fields(pdf_file)
405
- has_form_fields = bool(pdf_fields and "error" not in pdf_fields and pdf_fields)
406
-
407
- # เก็บ PDF ที่สร้าง
408
  generated_pdfs = {}
409
- success_count = 0
410
- error_count = 0
411
- processing_log = []
412
-
413
- # ประมวลผลแต่ละแถว
414
- for index, (_, row) in enumerate(df.iterrows()):
415
- progress((index + 1) / len(df), f"ประมวลผล {index + 1}/{len(df)}")
 
 
 
 
 
 
 
416
 
417
  try:
418
- # สร้างชื่อไฟล์
419
- if filename_column and filename_column in df.columns and pd.notna(row[filename_column]):
420
- safe_name = str(row[filename_column]).strip()
421
- safe_name = "".join(c for c in safe_name if c.isalnum() or c in (' ', '-', '_')).strip()
422
- filename = f"{file_prefix}_{safe_name}.pdf"
423
  else:
424
- filename = f"{file_prefix}_{index + 1:03d}.pdf"
425
-
426
- filename = filename.replace(' ', ' ').replace(' ', '_')
427
- if not filename.endswith('.pdf'):
428
- filename += '.pdf'
429
 
430
- # ประมวลผล
431
- pdf_content, status = process_single_row(
432
- pdf_file,
433
- row,
434
- filename,
435
- use_form_fields and has_form_fields
436
- )
437
-
438
- if pdf_content is not None:
439
- generated_pdfs[filename] = pdf_content
440
- success_count += 1
441
- processing_log.append(f"✅ {filename}: {status}")
442
- else:
443
- error_count += 1
444
- processing_log.append(f"❌ {filename}: {status}")
445
-
446
  except Exception as e:
447
- error_count += 1
448
- processing_log.append(f"💥 แถว {index + 1}: {str(e)}")
449
 
450
- # สร้าง ZIP
451
- if generated_pdfs:
452
- zip_buffer = io.BytesIO()
453
- with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
454
- for filename, pdf_content in generated_pdfs.items():
455
- zip_file.writestr(filename, pdf_content)
456
-
457
- zip_buffer.seek(0)
458
-
459
- # สร้างชื่อไฟล์ ZIP
460
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
461
- zip_filename = f"generated_pdfs_{timestamp}.zip"
462
-
463
- # บันทึกไฟล์ชั่วคราว
464
- temp_zip_path = os.path.join(tempfile.gettempdir(), zip_filename)
465
- with open(temp_zip_path, 'wb') as f:
466
- f.write(zip_buffer.getvalue())
467
-
468
- result_message = f"✅ สร้าง PDF สำเร็จ {success_count} ไฟล์!"
469
- if error_count > 0:
470
- result_message += f"\n⚠️ มีข้อผิดพลาด {error_count} ไฟล์"
471
 
472
- result_message += f"\n\n📋 รายละเอียด:\n" + "\n".join(processing_log[:10])
473
- if len(processing_log) > 10:
474
- result_message += f"\n... และอีก {len(processing_log) - 10} รายการ"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
 
476
- return temp_zip_path, result_message
477
- else:
478
- return None, "❌ ไม่สามารถสร้าง PDF ได้เลย"
 
 
 
 
 
 
479
 
 
480
  except Exception as e:
481
- return None, f"❌ เกิ���ข้อผิดพลาด: {str(e)}\n{traceback.format_exc()}"
 
 
 
 
 
482
 
483
  def analyze_pdf_info(pdf_file):
484
- """วิเคราะห์ข้อมูล PDF"""
485
  if pdf_file is None:
486
  return "ยังไม่มีไฟล์ PDF"
487
-
488
  try:
489
- reader = PdfReader(pdf_file)
490
- info = f"📄 **ข้อมูล PDF:**\n"
491
- info += f"- จำนวนหน้า: {len(reader.pages)}\n"
492
-
493
- # ตรวจสอบฟิลด์
494
- pdf_fields = analyze_pdf_fields(pdf_file)
495
-
496
- if pdf_fields and "error" not in pdf_fields and pdf_fields:
497
- info += f"- จำนวน Form Fields: {len(pdf_fields)}\n"
498
- info += f"\n🏷️ **รายชื่อ Fields:**\n"
499
- for name, details in list(pdf_fields.items())[:10]: # แสดงแค่ 10 ตัวแรก
500
- info += f" - {name} ({details.get('type', 'Unknown')})\n"
501
  if len(pdf_fields) > 10:
502
  info += f" - ... และอีก {len(pdf_fields) - 10} fields\n"
503
  else:
504
- info += "- Form Fields: ไม่พบหรือไม่สามารถอ่านได้\n"
505
- info += "- หมายเหตุ: จะสร้าง PDF ใหม่แทน\n"
506
-
507
  return info
508
  except Exception as e:
509
- return f"❌ ไม่สามารถวิเคราะห์ PDF ได้: {str(e)}"
510
 
511
  def analyze_csv_info(csv_file):
512
- """วิเคราะห์ข้อมูล CSV"""
513
  if csv_file is None:
514
- return "ยังไม่มีไฟล์ CSV"
515
-
516
  try:
517
  df, error = read_csv_safe(csv_file)
518
  if df is None:
519
- return f"❌ ไม่สามารถอ่าน CSV ได้: {error}"
520
 
521
- info = f"📋 **ข้อมูล CSV:**\n"
522
- info += f"- จำนวนแถว: {len(df)}\n"
523
- info += f"- จำนวนคอลัมน์: {len(df.columns)}\n"
524
- info += f"\n📝 **รายชื่อคอลัมน์:**\n"
525
-
526
- for col in df.columns[:15]: # แสดงแค่ 15 คอลัมน์แรก
527
- info += f" - {col}\n"
528
  if len(df.columns) > 15:
529
  info += f" - ... และอีก {len(df.columns) - 15} คอลัมน์\n"
530
 
531
- # ตรวจสอบข้อมูลที่ขาด
532
- missing_data = df.isnull().sum()
533
- if missing_data.any():
534
- missing_cols = missing_data[missing_data > 0]
535
- if len(missing_cols) > 0:
536
- info += f"\n⚠️ **ข้อมูลที่ขาดหาย:**\n"
537
- for col, count in missing_cols.head(5).items():
538
- info += f" - {col}: {count} แถว\n"
539
-
540
- return info
541
  except Exception as e:
542
- return f"❌ ไม่ส���มารถวิเคราะห์ CSV ได้: {str(e)}"
 
 
 
 
 
543
 
544
- # สร้าง Gradio Interface
545
  def create_interface():
546
  with gr.Blocks(title="PDF Form Filler & Template Generator", theme=gr.themes.Soft()) as app:
547
- gr.Markdown("""
548
- # 📄 เครื่องมือเติมข้อมูล PDF จาก CSV + สร้าง Template
549
-
550
- **เครื่องมือนี้สามารถ:**
551
- - 🔄 **สร้าง CSV/JSON Template จาก PDF** (Reverse Engineering)
552
- - 📝 เติมข้อมูลลงในฟอร์ม PDF ที่มี form fields
553
- - 🆕 สร้าง PDF ใหม่หากไม่มี form fields หรือเติมไม่ได้
554
- - 🌐 รองรับ CSV หลาย encoding (UTF-8, TIS-620, CP874, etc.)
555
- - 📦 ส่งออกเป็นไฟล์ ZIP
556
- """)
557
-
558
  with gr.Tabs():
559
- # Tab 1: Template Generator
560
- with gr.TabItem("🔄 สร้าง Template"):
561
- gr.Markdown("""
562
- ## 🔄 สร้าง CSV/JSON Template จาก PDF
563
- **อัพโหลด PDF ที่มี form fields แล้วสร้าง template ให้อัตโนมัติ**
564
- """)
565
-
566
  with gr.Row():
567
  with gr.Column(scale=1):
568
- template_pdf = gr.File(
569
- label="📄 PDF ที่ต้องการสร้าง Template",
570
- file_types=[".pdf"],
571
- type="filepath"
572
- )
573
-
574
- num_sample_rows = gr.Slider(
575
- label="จำนวนแถวตัวอย่างใน CSV",
576
- minimum=1,
577
- maximum=20,
578
- value=5,
579
- step=1
580
- )
581
-
582
- generate_template_btn = gr.Button(
583
- "🔄 สร้าง Template",
584
- variant="primary",
585
- size="lg"
586
- )
587
-
588
  with gr.Column(scale=2):
589
- template_pdf_info = gr.Markdown("ยังไม่มีไฟล์ PDF")
590
-
591
- template_result_file = gr.File(
592
- label="📦 ไฟล์ Template (ZIP)",
593
- visible=False
594
- )
595
-
596
- template_result_message = gr.Markdown("")
597
-
598
- # Tab 2: PDF Form Filler
599
- with gr.TabItem("📝 เติมข้อมูล PDF"):
600
- gr.Markdown("""
601
- ## 📝 เติมข้อมูล PDF จาก CSV
602
- **ใช้ CSV ที่สร้างจาก Template หรือ CSV ของคุณเอง**
603
- """)
604
-
605
  with gr.Row():
606
  with gr.Column(scale=1):
607
- gr.Markdown("### 📁 อัพโหลดไฟล์")
 
 
608
 
609
- pdf_file = gr.File(
610
- label="PDF Template",
611
- file_types=[".pdf"],
612
- type="filepath"
613
- )
614
 
615
- csv_file = gr.File(
616
- label="CSV Data",
617
- file_types=[".csv"],
618
- type="filepath"
619
- )
620
 
621
- gr.Markdown("### ⚙️ ตั้งค่
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
  import gradio as gr
3
  import pandas as pd
4
  import json
 
8
  import traceback
9
  import tempfile
10
  import os
11
+ import sys
12
+ import subprocess
13
 
14
+ # --- ตรวจสอบและติดตั้ง Dependencies ---
15
  try:
16
  from PyPDF2 import PdfReader, PdfWriter
17
  from reportlab.pdfgen import canvas
18
  from reportlab.lib.pagesizes import letter
19
  from reportlab.pdfbase import pdfmetrics
20
  from reportlab.pdfbase.ttfonts import TTFont
21
+ print("Dependencies หลักถูกติดตั้งเรียบร้อยแล้ว")
22
+ except ImportError:
23
+ print("กำลังติดตั้ง dependencies ที่จำเป็น: PyPDF2, reportlab, pandas")
 
24
  subprocess.check_call([sys.executable, "-m", "pip", "install", "PyPDF2", "reportlab", "pandas"])
25
  from PyPDF2 import PdfReader, PdfWriter
26
  from reportlab.pdfgen import canvas
27
  from reportlab.lib.pagesizes import letter
28
 
29
+ # --- Dependencies เสริมสำหรับ AI และ OCR (จะแจ้งเตือนถ้าไม่มี) ---
30
+ try:
31
+ from PIL import Image
32
+ import numpy as np
33
+ import cv2
34
+ import pytesseract
35
+ AI_OCR_ENABLED = True
36
+ print("Dependencies สำหรับ AI/OCR พร้อมใช้งาน")
37
+ except ImportError:
38
+ AI_OCR_ENABLED = False
39
+ print("คำเตือน: ไม่พบ Dependencies สำหรับ AI/OCR (Pillow, numpy, opencv-python, pytesseract)")
40
+ print("ฟังก์ชันที่เกี่ยวกับรูปภาพและ OCR จะไม่สามารถใช้งานได้")
41
+ print("ติดตั้งด้วย: pip install Pillow numpy opencv-python pytesseract และติดตั้ง Tesseract engine")
42
+
43
+ try:
44
+ from gradio_client import Client
45
+ SAMBANOVA_AI_ENABLED = True
46
+ print("Dependencies สำหรับ SambaNova AI พร้อมใช้งาน")
47
+ except ImportError:
48
+ SAMBANOVA_AI_ENABLED = False
49
+ print("คำเตือน: ไม่พบ Gradio Client (pip install 'gradio_client>=0.12.0')")
50
+ print("ฟังก์ชันที่ต้องใช้ AI Model จะไม่สามารถใช้งานได้")
51
+
52
+
53
+ # ==============================================================================
54
+ # ส่วนของฟังก์ชันหลัก (Core Functions)
55
+ # ==============================================================================
56
+
57
  def analyze_pdf_fields(pdf_path):
58
+ """วิเคราะห์ฟิลด์ใน PDF และคืนค่าเป็น Dictionary"""
59
  try:
60
  reader = PdfReader(pdf_path)
61
  all_fields = {}
 
62
  # ตรวจสอบจาก AcroForm
63
  if reader.trailer.get("/Root") and reader.trailer["/Root"].get("/AcroForm"):
64
  acro_form = reader.trailer["/Root"]["/AcroForm"]
65
  if "/Fields" in acro_form:
66
+ for field in acro_form["/Fields"]:
 
67
  field_obj = field.get_object()
68
  if "/T" in field_obj:
69
  field_name = str(field_obj["/T"]).strip("()")
 
74
  'default_value': field_value,
75
  'method': 'AcroForm'
76
  }
77
+ # ตรวจสอบจาก Annotations ในแต่ละหน้า
 
78
  for page_num, page in enumerate(reader.pages):
79
  if "/Annots" in page:
80
+ for annotation in page["/Annots"]:
81
+ annot_obj = annotation.get_object()
82
+ if annot_obj.get("/Subtype") == "/Widget" and "/T" in annot_obj:
83
+ field_name = str(annot_obj["/T"]).strip("()")
84
+ if field_name not in all_fields: # เพิ่มเฉพาะที่ยังไม่มี
85
+ field_type = str(annot_obj.get("/FT", "Widget"))
86
+ field_value = str(annot_obj.get("/V", "")).strip("()")
87
+ all_fields[field_name] = {
88
+ 'type': field_type,
89
+ 'default_value': field_value,
90
+ 'page': page_num + 1,
91
+ 'method': 'Annotation'
92
+ }
 
 
 
 
 
93
  return all_fields
94
  except Exception as e:
95
  return {"error": str(e)}
 
98
  """สร้าง CSV template จาก PDF fields"""
99
  if not pdf_fields or "error" in pdf_fields:
100
  return None, "ไม่สามารถสร้าง CSV template ได้"
101
+ template_data = {'id': list(range(1, num_rows + 1))}
102
+ for field_name in pdf_fields.keys():
 
 
 
 
 
 
 
103
  if field_name and field_name.strip():
104
  clean_name = field_name.strip()
105
+ sample_value = f"ข้อมูลสำหรับ {clean_name} {{}}"
106
+ template_data[clean_name] = [sample_value.format(i) for i in range(1, num_rows + 1)]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  df = pd.DataFrame(template_data)
108
  return df, "สร้าง CSV template สำเร็จ"
109
 
 
111
  """สร้าง JSON template จาก PDF fields"""
112
  if not pdf_fields or "error" in pdf_fields:
113
  return None, "ไม่สามารถสร้าง JSON template ได้"
 
114
  template = {
115
+ "pdf_info": {"total_fields": len(pdf_fields), "generation_time": datetime.now().isoformat()},
 
 
 
116
  "fields": {},
117
  "sample_data": []
118
  }
 
 
119
  for field_name, field_info in pdf_fields.items():
120
  if field_name and field_name.strip():
121
+ template["fields"][field_name.strip()] = field_info
122
+ for i in range(1, 4):
 
 
 
 
 
 
 
 
123
  sample_record = {"id": i}
124
  for field_name in template["fields"].keys():
125
+ sample_record[field_name] = f"ข้อมูลตัวอย่าง {i}"
 
 
 
 
 
 
 
126
  template["sample_data"].append(sample_record)
 
127
  return template, "สร้าง JSON template สำเร็จ"
128
 
129
+ def create_template_files(pdf_file, num_rows, progress=gr.Progress()):
130
+ """สร้างไฟล์ template (CSV, JSON, README) และรวมเป็น ZIP"""
131
  if pdf_file is None:
132
+ return None, "❌ กรุณาอัพโหลดไฟล์ PDF ก่อน"
133
 
134
+ progress(0, desc="กำลังวิเคราะห์ PDF...")
135
  try:
136
+ pdf_fields = analyze_pdf_fields(pdf_file.name)
 
 
137
  if not pdf_fields or "error" in pdf_fields:
138
+ return None, "❌ ไม่พบ Form Fields ใน PDF หรือไฟล์เสียหาย"
139
 
140
+ progress(0.3, desc="กำลังสร้าง CSV template...")
141
+ csv_df, _ = generate_csv_template(pdf_fields, num_rows)
142
 
143
+ progress(0.6, desc="กำลังสร้าง JSON template...")
144
+ json_template, _ = generate_json_template(pdf_fields)
145
 
146
  if csv_df is None or json_template is None:
147
+ return None, "❌ ไม่สามารถสร้างไฟล์ template ได้"
148
 
149
+ progress(0.8, desc="กำลังบีบอัดไฟล์เป็น ZIP...")
150
  zip_buffer = io.BytesIO()
151
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
152
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_f:
 
 
153
  csv_buffer = io.StringIO()
154
  csv_df.to_csv(csv_buffer, index=False, encoding='utf-8-sig')
155
+ zip_f.writestr(f"template_{timestamp}.csv", csv_buffer.getvalue())
156
 
 
157
  json_str = json.dumps(json_template, ensure_ascii=False, indent=2)
158
+ zip_f.writestr(f"template_{timestamp}.json", json_str)
159
 
 
160
  readme_content = f"""# PDF Form Template Files
161
  Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
162
+ PDF Fields Found: {len(pdf_fields)}
163
+ {chr(10).join([f"- {name}" for name in pdf_fields.keys()])}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  """
165
+ zip_f.writestr("README.txt", readme_content)
166
 
167
  zip_buffer.seek(0)
168
 
169
+ # บันทึกไฟล์ ZIP ชั่วคราวเพื่อให้ Gradio ส่งให้ผู้ใช้ได้
170
+ temp_dir = tempfile.gettempdir()
171
+ zip_filename = f"pdf_templates_{timestamp}.zip"
172
+ temp_zip_path = os.path.join(temp_dir, zip_filename)
173
+ with open(temp_zip_path, "wb") as f:
174
  f.write(zip_buffer.getvalue())
175
 
176
+ progress(1, desc="สร้างไฟล์สำเร็จ!")
177
+ result_msg = f" สร้าง template สำเร็จ!\n- พบ {len(pdf_fields)} fields\n- CSV มี {num_rows} แถวตัวอย่าง"
 
 
 
178
 
179
+ return temp_zip_path, result_msg
180
 
181
  except Exception as e:
182
+ return None, f"❌ เกิดข้อผิดพลาด: {e}\n{traceback.format_exc()}"
183
 
184
  def fill_pdf_form(pdf_path, field_data):
185
+ """เติมข้อมูลลงในฟอร์มของ PDF"""
186
+ reader = PdfReader(pdf_path)
187
+ writer = PdfWriter()
188
+ writer.append_pages_from_reader(reader)
189
+
190
+ # เติมข้อมูลในฟอร์ม
191
+ for page in writer.pages:
192
+ try:
193
+ writer.update_page_form_field_values(page, field_data, auto_regenerate=False)
194
+ except Exception:
195
+ # บางครั้ง field อยูในระดับ root
196
+ pass
197
+ try: # ลองเติมที่ root อีกครั้ง
198
+ writer.update_page_form_field_values(writer.pages[0], field_data)
199
+ except:
200
+ pass
201
+
202
+ output_buffer = io.BytesIO()
203
+ writer.write(output_buffer)
204
+ output_buffer.seek(0)
205
+ return output_buffer.getvalue()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
  def create_simple_pdf(data_row, filename):
208
+ """สร้าง PDF ใหม่แบบง่ายๆ กรณีที่ PDF ต้นฉบับไม่มีฟอร์ม"""
209
  buffer = io.BytesIO()
210
+ # ใช้ font ที่รองรับภาษาไทย
211
+ try:
212
+ pdfmetrics.registerFont(TTFont('THSarabunNew', 'THSarabunNew.ttf'))
213
+ font_name = 'THSarabunNew'
214
+ except:
215
+ font_name = 'Helvetica' # Fallback
216
+
217
  p = canvas.Canvas(buffer, pagesize=letter)
218
  width, height = letter
219
 
220
+ p.setFont(font_name, 16)
221
+ p.drawString(50, height - 50, f"เอกสาร: {filename.replace('.pdf', '')}")
 
 
 
 
 
222
  p.line(50, height - 60, 550, height - 60)
223
 
224
+ y_position = height - 90
225
+ p.setFont(font_name, 12)
 
 
226
  for column, value in data_row.items():
227
  if pd.notna(value) and str(value).strip():
228
+ text = f"{str(column).strip()}: {str(value).strip()}"
 
 
 
 
 
 
 
229
  try:
230
  p.drawString(50, y_position, text)
231
  except:
232
+ safe_text = text.encode('latin-1', 'replace').decode('latin-1')
233
  p.drawString(50, y_position, safe_text)
234
+ y_position -= 20
 
 
235
  if y_position < 50:
236
  p.showPage()
237
+ p.setFont(font_name, 12)
238
  y_position = height - 50
239
 
 
 
 
 
 
240
  p.save()
241
  buffer.seek(0)
242
  return buffer.getvalue()
243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  def read_csv_safe(csv_file):
245
+ """อ่านไฟล์ CSV โดยลองหลาย encoding และ separator เพื่อความยืดหยุ่น"""
246
+ encodings = ['utf-8-sig', 'utf-8', 'cp874', 'tis-620']
247
+ separators = [',', ';', '\t']
248
 
249
+ # ใช้ .name เพราะ Gradio ส่งมาเป็น object ที่มี path อยู่ใน .name
250
+ filepath = csv_file.name
251
+
252
  for encoding in encodings:
253
  for sep in separators:
254
  try:
255
+ df = pd.read_csv(filepath, encoding=encoding, sep=sep, engine='python')
256
+ if len(df.columns) > 1:
257
  return df, None
258
  except Exception:
259
  continue
260
+ return None, "ไม่สามารถอ่านไฟล์ CSV ได้ ลองตรวจสอบ Encoding (ควรเป็น UTF-8) และ Separator (ควรเป็น ,)"
 
 
 
 
 
261
 
262
  def process_pdf_csv(pdf_file, csv_file, filename_column, file_prefix, use_form_fields, progress=gr.Progress()):
263
  """ฟังก์ชันหลักสำหรับประมวลผล PDF และ CSV"""
264
+ if not pdf_file or not csv_file:
265
+ return None, "❌ กรุณาอัพโหลดทั้งไฟล์ PDF และ CSV"
266
+
 
267
  try:
 
268
  df, csv_error = read_csv_safe(csv_file)
269
  if df is None:
270
  return None, f"❌ ไม่สามารถอ่าน CSV ได้: {csv_error}"
271
+
272
+ pdf_path = pdf_file.name
273
+ pdf_fields = analyze_pdf_fields(pdf_path)
274
+ has_form_fields = bool(pdf_fields and "error" not in pdf_fields)
275
 
 
 
 
 
 
276
  generated_pdfs = {}
277
+ log = []
278
+ total_rows = len(df)
279
+
280
+ for index, row in df.iterrows():
281
+ progress((index + 1) / total_rows, f"ประมวลผลแถวที่ {index + 1}/{total_rows}")
282
+
283
+ # สร้างชื่อไฟล์
284
+ if filename_column and filename_column in df.columns and pd.notna(row[filename_column]):
285
+ safe_name = "".join(c for c in str(row[filename_column]) if c.isalnum() or c in (' ', '-', '_')).strip()
286
+ filename = f"{file_prefix}_{safe_name}.pdf"
287
+ else:
288
+ filename = f"{file_prefix}_{index + 1:03d}.pdf"
289
+
290
+ row_data = row.to_dict()
291
 
292
  try:
293
+ if use_form_fields and has_form_fields:
294
+ # เติมฟอร์ม PDF ที่มีอยู่
295
+ pdf_content = fill_pdf_form(pdf_path, row_data)
296
+ status = "เติมฟอร์มสำเร็จ"
 
297
  else:
298
+ # สร้าง PDF ใหม่
299
+ pdf_content = create_simple_pdf(row_data, filename)
300
+ status = "สร้าง PDF ใหม่" if not has_form_fields else "สร้าง PDF ใหม่ (Fallback)"
 
 
301
 
302
+ generated_pdfs[filename] = pdf_content
303
+ log.append(f"✅ {filename}: {status}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  except Exception as e:
305
+ log.append(f"❌ {filename}: เกิดข้อผิดพลาด - {e}")
 
306
 
307
+ if not generated_pdfs:
308
+ return None, "❌ ไม่สามารถสร้าง PDF ได้เลย\n" + "\n".join(log)
309
+
310
+ # สร้างไฟล์ ZIP
311
+ zip_buffer = io.BytesIO()
312
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_f:
313
+ for filename, pdf_content in generated_pdfs.items():
314
+ zip_f.writestr(filename, pdf_content)
315
+ zip_f.writestr("processing_log.txt", "\n".join(log))
316
+ zip_buffer.seek(0)
317
+
318
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
319
+ zip_filename = f"generated_pdfs_{timestamp}.zip"
320
+ temp_zip_path = os.path.join(tempfile.gettempdir(), zip_filename)
321
+ with open(temp_zip_path, 'wb') as f:
322
+ f.write(zip_buffer.getvalue())
 
 
 
 
 
323
 
324
+ result_message = f" สร้าง PDF สำเร็จ {len(generated_pdfs)} ไฟล์!\nดูรายละเอียดใน processing_log.txt"
325
+ return temp_zip_path, result_message
326
+
327
+ except Exception as e:
328
+ return None, f"❌ เกิดข้อผิดพลาดร้ายแรง: {e}\n{traceback.format_exc()}"
329
+
330
+
331
+ # ==============================================================================
332
+ # ส่วนของฟังก์ชัน AI และ OCR (ทางเลือก)
333
+ # ==============================================================================
334
+
335
+ def init_sambanova_ai():
336
+ """Initialize SambaNova AI model client."""
337
+ if not SAMBANOVA_AI_ENABLED:
338
+ print("SambaNova AI is disabled.")
339
+ return None
340
+ try:
341
+ # ใช้ gradio_client.Client แทน gr.load ที่อาจมีปัญหา
342
+ client = Client("sambanova/Llama-3-8B-Instruct", hf_token="YOUR_HF_TOKEN") # ใส่ Hugging Face Token ของคุณ
343
+ print("SambaNova AI client initialized successfully.")
344
+ return client
345
+ except Exception as e:
346
+ print(f"Error initializing SambaNova AI: {e}")
347
+ return None
348
+
349
+ def extract_text_from_image(image_file):
350
+ """Extract text from an image file using Tesseract OCR."""
351
+ if not AI_OCR_ENABLED or image_file is None:
352
+ return "", "OCR is not available or no image provided."
353
+ try:
354
+ image = Image.open(image_file.name)
355
+ # ตั้งค่า Tesseract ให้ตรวจจับทั้งภาษาไทยและอังกฤษ
356
+ custom_config = r'--oem 3 --psm 6 -l tha+eng'
357
+ text = pytesseract.image_to_string(image, config=custom_config)
358
+ return text.strip(), "Text extracted successfully."
359
+ except Exception as e:
360
+ return "", f"OCR Error: {e}. ตรวจสอบว่าติดตั้ง Tesseract Engine ถูกต้อง"
361
+
362
+ def image_to_csv_with_ai(image_file, progress=gr.Progress()):
363
+ """Convert data from an image to a CSV file using OCR and AI for structuring."""
364
+ if not AI_OCR_ENABLED:
365
+ return None, "❌ ฟังก์ชันนี้ต้องการ AI/OCR dependencies"
366
+ if image_file is None:
367
+ return None, "❌ กรุณาอัพโหลดรูปภาพ"
368
+
369
+ progress(0.2, desc="กำลังอ่านข้อความจากรูปภาพ (OCR)...")
370
+ raw_text, ocr_status = extract_text_from_image(image_file)
371
+ if not raw_text:
372
+ return None, f"❌ ไม่พบข้อความในรูปภาพ: {ocr_status}"
373
+
374
+ progress(0.5, desc="กำลังใช้ AI จัดโครงสร้างข้อมูล...")
375
+ ai_client = init_sambanova_ai()
376
+ if not ai_client:
377
+ return None, "❌ ไม่สามารถเชื่อมต่อ AI Model ได้"
378
+
379
+ prompt = f"""
380
+ From the following text, extract key-value pairs. The output should be only the data in 'key: value' format, one per line.
381
+ Example:
382
+ Name: John Doe
383
+ Address: 123 Main St
384
+ Date: 2024-01-15
385
+
386
+ Text to process:
387
+ ---
388
+ {raw_text}
389
+ ---
390
+ """
391
+ try:
392
+ # การเรียกใช้งาน API ของ gradio_client
393
+ result = ai_client.predict(message=prompt, api_name="/chat")
394
+
395
+ progress(0.8, desc="กำลังสร้างไฟล์ CSV...")
396
+ lines = result.strip().split('\n')
397
+ data = [line.split(':', 1) for line in lines if ':' in line]
398
+
399
+ if not data:
400
+ return None, "AI ไม่สามารถจัดโครงสร้างข้อมูลได้"
401
 
402
+ df = pd.DataFrame(data, columns=['Field', 'Value']).set_index('Field').T
403
+
404
+ csv_buffer = io.StringIO()
405
+ df.to_csv(csv_buffer, index=False, encoding='utf-8-sig')
406
+
407
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
408
+ temp_csv_path = os.path.join(tempfile.gettempdir(), f"extracted_data_{timestamp}.csv")
409
+ with open(temp_csv_path, 'w', encoding='utf-8-sig') as f:
410
+ f.write(csv_buffer.getvalue())
411
 
412
+ return temp_csv_path, "✅ แปลงรูปภาพเป็น CSV สำเร็จ"
413
  except Exception as e:
414
+ return None, f"❌ เกิดข้อผิดพลาดระหว่างประมวลผลด้วย AI: {e}"
415
+
416
+
417
+ # ==============================================================================
418
+ # ส่วนของ UI Analysis Functions
419
+ # ==============================================================================
420
 
421
  def analyze_pdf_info(pdf_file):
422
+ """วิเคราะห์และแสดงข้อมูลสรุปของไฟล์ PDF บน UI"""
423
  if pdf_file is None:
424
  return "ยังไม่มีไฟล์ PDF"
 
425
  try:
426
+ reader = PdfReader(pdf_file.name)
427
+ info = f"📄 **ข้อมูล PDF:**\n- จำนวนหน้า: {len(reader.pages)}\n"
428
+ pdf_fields = analyze_pdf_fields(pdf_file.name)
429
+ if pdf_fields and "error" not in pdf_fields:
430
+ info += f"- **พบ Form Fields: {len(pdf_fields)} ช่อง** (จะใช้วิธีเติมฟอร์ม)\n"
431
+ info += "\n🏷️ **ตัวอย่างชื่อ Fields:**\n"
432
+ for name in list(pdf_fields.keys())[:10]:
433
+ info += f" - `{name}`\n"
 
 
 
 
434
  if len(pdf_fields) > 10:
435
  info += f" - ... และอีก {len(pdf_fields) - 10} fields\n"
436
  else:
437
+ info += "- **ไม่พบ Form Fields** (จะใช้วิธีสร้าง PDF ใหม่ทับลงบนกระดาษเปล่า)\n"
 
 
438
  return info
439
  except Exception as e:
440
+ return f"❌ ไม่สามารถวิเคราะห์ PDF: {e}"
441
 
442
  def analyze_csv_info(csv_file):
443
+ """วิเคราะห์และแสดงข้อมูลสรุปของไฟล์ CSV และอัปเดต Dropdown"""
444
  if csv_file is None:
445
+ return "ยังไม่มีไฟล์ CSV", gr.update(choices=[], value=None)
 
446
  try:
447
  df, error = read_csv_safe(csv_file)
448
  if df is None:
449
+ return f"❌ ไม่สามารถอ่าน CSV: {error}", gr.update(choices=[], value=None)
450
 
451
+ info = f"📋 **ข้อมูล CSV:**\n- จำนวนแถว: {len(df)}\n- จำนวนคอลัมน์: {len(df.columns)}\n"
452
+ info += "\n📝 **รายชื่อคอลัมน์:**\n"
453
+ for col in df.columns[:15]:
454
+ info += f" - `{col}`\n"
 
 
 
455
  if len(df.columns) > 15:
456
  info += f" - ... และอีก {len(df.columns) - 15} คอลัมน์\n"
457
 
458
+ # อัปเดต Dropdown สำหรับเลือกคอลัมน์ชื่อไฟล์
459
+ return info, gr.update(choices=df.columns.tolist(), value=None)
 
 
 
 
 
 
 
 
460
  except Exception as e:
461
+ return f"❌ ไม่สามารถวิเคราะห์ CSV: {e}", gr.update(choices=[], value=None)
462
+
463
+
464
+ # ==============================================================================
465
+ # ส่วนของการสร้าง Gradio Interface
466
+ # ==============================================================================
467
 
 
468
  def create_interface():
469
  with gr.Blocks(title="PDF Form Filler & Template Generator", theme=gr.themes.Soft()) as app:
470
+ gr.Markdown("# 📄 เครื่องมือจัดการ PDF จากข้อมูล CSV")
471
+ gr.Markdown("รองรับการ **สร้าง Template** จาก PDF, **เติมข้อมูล** จาก CSV, และ **แปลงรูปภาพเป็น CSV** ด้วย AI")
472
+
 
 
 
 
 
 
 
 
473
  with gr.Tabs():
474
+ # --- Tab 1: สร้าง Template ---
475
+ with gr.TabItem("🔄 1. สร้าง Template"):
476
+ gr.Markdown("## สร้าง CSV/JSON Template จาก PDF ที่มี Form Fields")
 
 
 
 
477
  with gr.Row():
478
  with gr.Column(scale=1):
479
+ template_pdf = gr.File(label="📄 อัพโหลด PDF ต้นฉบับ", file_types=[".pdf"])
480
+ num_sample_rows = gr.Slider(label="จำนวนแถวตัวอย่างใน CSV", minimum=1, maximum=50, value=5, step=1)
481
+ generate_template_btn = gr.Button("🚀 สร้าง Template", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
  with gr.Column(scale=2):
483
+ template_pdf_info = gr.Markdown("อัพโหลด PDF เพื่อดูข้อมูล...")
484
+ template_result_file = gr.File(label="📦 ดาวน์โหลดไฟล์ Template (ZIP)", interactive=False)
485
+ template_result_message = gr.Markdown()
486
+
487
+ # --- Tab 2: เติมข้อมูล PDF ---
488
+ with gr.TabItem("📝 2. เติมข้อมูล PDF"):
489
+ gr.Markdown("## เติมข้อมูลลง��น PDF จากไฟล์ CSV")
 
 
 
 
 
 
 
 
 
490
  with gr.Row():
491
  with gr.Column(scale=1):
492
+ gr.Markdown("### 📂 1. อัพโหลดไฟล์")
493
+ pdf_file = gr.File(label="📄 PDF Form ต้นฉบับ", file_types=[".pdf"])
494
+ csv_file = gr.File(label="📊 CSV ข้อมูล", file_types=[".csv"])
495
 
496
+ gr.Markdown("### ⚙️ 2. ตั้งค่า")
497
+ use_form_fields = gr.Checkbox(label="พยายามเติมข้อมูลลงใน Form Fields ที่มีอยู่", value=True)
498
+ file_prefix = gr.Textbox(label="คำนำหน้าชื่อไฟล์ (Prefix)", value="Document")
499
+ filename_column = gr.Dropdown(label="เลือกคอลัมน์ที่จะใช้เป็นชื่อไฟล์ (ถ้ามี)", interactive=True)
 
500
 
501
+ fill_form_btn = gr.Button("🚀 เริ่มเติมข้อมูล", variant="primary")
 
 
 
 
502
 
503
+ with gr.Column(scale=2):
504
+ pdf_info = gr.Markdown("อัพโหลด PDF เพื่อดูข้อมูล...")
505
+ csv_info = gr.Markdown("อัพโหลด CSV เพื่อดูข้อมูล...")
506
+ gr.Markdown("---")
507
+ filled_result_file = gr.File(label="📦 ดาวน์โหลด PDF ทั้งหมด (ZIP)", interactive=False)
508
+ filled_result_message = gr.Markdown()
509
+
510
+ # --- Tab 3: Image to CSV (AI) ---
511
+ with gr.TabItem("🖼️ 3. แปลงรูปภาพเป็น CSV (AI)"):
512
+ gr.Markdown("## ใช้ OCR และ AI เพื่อดึงข้อมูลจากรูปภาพและสร้างเป็นไฟล์ CSV")
513
+ with gr.Row():
514
+ with gr.Column(scale=1):
515
+ image_upload = gr.File(label="🖼️ อัพโหลดรูปภาพ (บิล, เอกสาร, ฯลฯ)", file_types=["image"])
516
+ image_to_csv_btn = gr.Button("🤖 แปลงเป็น CSV", variant="primary", visible=AI_OCR_ENABLED)
517
+ if not AI_OCR_ENABLED:
518
+ gr.Markdown("⚠️ *ฟังก์ชันนี้ถูกปิดใช้งานเนื่องจากไม่พบ Library ที่จำเป็น (Pillow, OpenCV, Pytesseract)*")
519
+
520
+ with gr.Column(scale=2):
521
+ image_csv_output = gr.File(label="📄 ดาวน์โหลดไฟล์ CSV ที่ได้", interactive=False)
522
+ image_csv_message = gr.Markdown()
523
+
524
+ # --- Event Handlers ---
525
+ template_pdf.change(fn=analyze_pdf_info, inputs=template_pdf, outputs=template_pdf_info)
526
+ generate_template_btn.click(
527
+ fn=create_template_files,
528
+ inputs=[template_pdf, num_sample_rows],
529
+ outputs=[template_result_file, template_result_message]
530
+ )
531
+
532
+ pdf_file.change(fn=analyze_pdf_info, inputs=pdf_file, outputs=pdf_info)
533
+ csv_file.change(fn=analyze_csv_info, inputs=csv_file, outputs=[csv_info, filename_column])
534
+
535
+ fill_form_btn.click(
536
+ fn=process_pdf_csv,
537
+ inputs=[pdf_file, csv_file, filename_column, file_prefix, use_form_fields],
538
+ outputs=[filled_result_file, filled_result_message]
539
+ )
540
+
541
+ if AI_OCR_ENABLED:
542
+ image_to_csv_btn.click(
543
+ fn=image_to_csv_with_ai,
544
+ inputs=[image_upload],
545
+ outputs=[image_csv_output, image_csv_message]
546
+ )
547
+
548
+ return app
549
+
550
+ # --- Launch the application ---
551
+ if __name__ == "__main__":
552
+ # ลองหา font ไทย ถ้าไม่มีจะได้ไม่ error ตอนสร้าง PDF
553
+ try:
554
+ from reportlab.pdfbase import pdfmetrics
555
+ from reportlab.pdfbase.ttfonts import TTFont
556
+ # สำหรับ Windows
557
+ pdfmetrics.registerFont(TTFont('THSarabunNew', 'C:/Windows/Fonts/THSARI.TTF'))
558
+ print("ลงทะเบียน Font 'THSarabunNew' สำห���ับ ReportLab สำเร็จ")
559
+ except:
560
+ print("คำเตือน: ไม่พบ Font 'THSarabunNew' ในระบบ อาจทำให้การสร้าง PDF ภาษาไทยมีปัญหา")
561
+ print("แนะนำให้ติดตั้งฟอนต์ TH SarabunPSK หรือปรับแก้ path ของฟอนต์ในโค้ด")
562
+
563
+ app = create_interface()
564
+ app.launch(debug=True)