protae5544 commited on
Commit
fd34976
·
verified ·
1 Parent(s): 1482483

Create pdfk.py

Browse files
Files changed (1) hide show
  1. pdfk.py +486 -0
pdfk.py ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import io
4
+ import zipfile
5
+ from datetime import datetime
6
+ import traceback
7
+ import tempfile
8
+ import os
9
+
10
+ # ติดตั้ง dependencies ที่จำเป็น
11
+ try:
12
+ from PyPDF2 import PdfReader, PdfWriter
13
+ from reportlab.pdfgen import canvas
14
+ from reportlab.lib.pagesizes import letter
15
+ from reportlab.pdfbase import pdfmetrics
16
+ from reportlab.pdfbase.ttfonts import TTFont
17
+ except ImportError as e:
18
+ print(f"กำลังติดตั้ง dependencies: {e}")
19
+ import subprocess
20
+ import sys
21
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "PyPDF2", "reportlab", "pandas"])
22
+ from PyPDF2 import PdfReader, PdfWriter
23
+ from reportlab.pdfgen import canvas
24
+ from reportlab.lib.pagesizes import letter
25
+
26
+ def analyze_pdf_fields(pdf_path):
27
+ """วิเคราะห์ฟิลด์ใน PDF"""
28
+ try:
29
+ reader = PdfReader(pdf_path)
30
+ all_fields = {}
31
+
32
+ # ตรวจสอบจาก AcroForm
33
+ if reader.trailer.get("/Root") and reader.trailer["/Root"].get("/AcroForm"):
34
+ acro_form = reader.trailer["/Root"]["/AcroForm"]
35
+ if "/Fields" in acro_form:
36
+ fields = acro_form["/Fields"]
37
+ for field in fields:
38
+ field_obj = field.get_object()
39
+ if "/T" in field_obj:
40
+ field_name = str(field_obj["/T"]).strip("()")
41
+ field_type = str(field_obj.get("/FT", "Unknown"))
42
+ all_fields[field_name] = {
43
+ 'type': field_type,
44
+ 'method': 'AcroForm'
45
+ }
46
+
47
+ # ตรวจสอบจาก Annotations
48
+ for page_num, page in enumerate(reader.pages):
49
+ if "/Annots" in page:
50
+ try:
51
+ annotations = page["/Annots"]
52
+ for annotation in annotations:
53
+ annot_obj = annotation.get_object()
54
+ if annot_obj.get("/Subtype") == "/Widget":
55
+ if "/T" in annot_obj:
56
+ field_name = str(annot_obj["/T"]).strip("()")
57
+ field_type = str(annot_obj.get("/FT", "Widget"))
58
+ all_fields[field_name] = {
59
+ 'type': field_type,
60
+ 'page': page_num + 1,
61
+ 'method': 'Annotation'
62
+ }
63
+ except Exception:
64
+ continue
65
+
66
+ return all_fields
67
+ except Exception as e:
68
+ return {"error": str(e)}
69
+
70
+ def fill_pdf_form(pdf_path, field_data):
71
+ """เติมข้อมูลในฟอร์ม PDF"""
72
+ try:
73
+ reader = PdfReader(pdf_path)
74
+ writer = PdfWriter()
75
+
76
+ # คัดลอกหน้าทั้งหมด
77
+ for page in reader.pages:
78
+ writer.add_page(page)
79
+
80
+ # เติมข้อมูลในฟอร์ม
81
+ if hasattr(writer, 'update_page_form_field_values'):
82
+ for page_num, page in enumerate(writer.pages):
83
+ try:
84
+ writer.update_page_form_field_values(page, field_data)
85
+ except Exception:
86
+ pass
87
+
88
+ # ลองวิธีอื่น
89
+ elif "/AcroForm" in reader.trailer.get("/Root", {}):
90
+ try:
91
+ acro_form = reader.trailer["/Root"]["/AcroForm"]
92
+ if "/Fields" in acro_form:
93
+ fields = acro_form["/Fields"]
94
+ for field in fields:
95
+ field_obj = field.get_object()
96
+ if "/T" in field_obj:
97
+ field_name = str(field_obj["/T"]).strip("()")
98
+ if field_name in field_data:
99
+ try:
100
+ field_obj.update({"/V": field_data[field_name]})
101
+ except Exception:
102
+ pass
103
+ except Exception:
104
+ pass
105
+
106
+ return writer
107
+ except Exception as e:
108
+ raise Exception(f"ไม่สามารถเติมฟอร์มได้: {str(e)}")
109
+
110
+ def create_simple_pdf(data_row, filename):
111
+ """สร้าง PDF ใหม่แบบง่าย"""
112
+ buffer = io.BytesIO()
113
+ p = canvas.Canvas(buffer, pagesize=letter)
114
+ width, height = letter
115
+
116
+ # ตั้งค่า font
117
+ p.setFont("Helvetica", 12)
118
+
119
+ # หัวเรื่อง
120
+ p.setFont("Helvetica-Bold", 16)
121
+ title = f"Document: {filename.replace('.pdf', '')}"
122
+ p.drawString(50, height - 50, title)
123
+ p.line(50, height - 60, 550, height - 60)
124
+
125
+ # เนื้อหา
126
+ y_position = height - 100
127
+ p.setFont("Helvetica", 12)
128
+
129
+ for column, value in data_row.items():
130
+ if pd.notna(value) and str(value).strip():
131
+ clean_column = str(column).strip()
132
+ clean_value = str(value).strip()
133
+
134
+ if len(clean_value) > 80:
135
+ clean_value = clean_value[:77] + "..."
136
+
137
+ text = f"{clean_column}: {clean_value}"
138
+
139
+ try:
140
+ p.drawString(50, y_position, text)
141
+ except:
142
+ safe_text = text.encode('ascii', errors='ignore').decode('ascii')
143
+ p.drawString(50, y_position, safe_text)
144
+
145
+ y_position -= 25
146
+
147
+ if y_position < 50:
148
+ p.showPage()
149
+ p.setFont("Helvetica", 12)
150
+ y_position = height - 50
151
+
152
+ # เวลาที่สร้าง
153
+ p.setFont("Helvetica", 8)
154
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
155
+ p.drawString(50, 30, f"Created: {timestamp}")
156
+
157
+ p.save()
158
+ buffer.seek(0)
159
+ return buffer.getvalue()
160
+
161
+ def process_single_row(pdf_path, row_data, filename, use_form=True):
162
+ """ประมวลผลแถวเดียว"""
163
+ try:
164
+ # เตรียมข้อมูลฟิลด์
165
+ field_data = {}
166
+ for column, value in row_data.items():
167
+ if pd.notna(value) and str(value).strip():
168
+ clean_value = str(value).strip()
169
+ clean_column = str(column).strip()
170
+
171
+ # ลองหลายรูปแบบของชื่อฟิลด์
172
+ field_variations = [
173
+ clean_column,
174
+ clean_column.lower(),
175
+ clean_column.upper(),
176
+ clean_column.replace('_', ' '),
177
+ clean_column.replace(' ', '_'),
178
+ clean_column.replace('-', '_'),
179
+ clean_column.replace('_', '')
180
+ ]
181
+
182
+ for variation in field_variations:
183
+ field_data[variation] = clean_value
184
+
185
+ if use_form:
186
+ try:
187
+ # ลองเติมฟอร์ม
188
+ writer = fill_pdf_form(pdf_path, field_data)
189
+
190
+ output_buffer = io.BytesIO()
191
+ writer.write(output_buffer)
192
+ output_buffer.seek(0)
193
+ return output_buffer.getvalue(), "form_filled"
194
+ except Exception as e:
195
+ # ถ้าไม่ได้ ให้สร้างใหม่
196
+ pdf_content = create_simple_pdf(row_data, filename)
197
+ return pdf_content, f"new_pdf_created: {str(e)}"
198
+ else:
199
+ # สร้าง PDF ใหม่
200
+ pdf_content = create_simple_pdf(row_data, filename)
201
+ return pdf_content, "new_pdf_created"
202
+
203
+ except Exception as e:
204
+ return None, f"error: {str(e)}"
205
+
206
+ def read_csv_safe(csv_file):
207
+ """อ่าน CSV อย่างปลอดภัย"""
208
+ encodings = ['utf-8', 'utf-8-sig', 'cp874', 'tis-620', 'iso-8859-1', 'cp1252']
209
+ separators = [',', ';', '\t', '|']
210
+
211
+ for encoding in encodings:
212
+ for sep in separators:
213
+ try:
214
+ df = pd.read_csv(csv_file, encoding=encoding, sep=sep, engine='python')
215
+ if len(df.columns) > 1 and len(df) > 0:
216
+ return df, None
217
+ except Exception:
218
+ continue
219
+
220
+ try:
221
+ df = pd.read_csv(csv_file)
222
+ return df, None
223
+ except Exception as e:
224
+ return None, str(e)
225
+
226
+ def process_pdf_csv(pdf_file, csv_file, filename_column, file_prefix, use_form_fields, progress=gr.Progress()):
227
+ """ฟังก์ชันหลักสำหรับประมวลผล PDF และ CSV"""
228
+
229
+ if pdf_file is None or csv_file is None:
230
+ return None, "❌ กรุณาอัพโหลดไฟล์ PDF และ CSV"
231
+
232
+ try:
233
+ # อ่าน CSV
234
+ df, csv_error = read_csv_safe(csv_file)
235
+ if df is None:
236
+ return None, f"❌ ไม่สามารถอ่าน CSV ได้: {csv_error}"
237
+
238
+ # วิเคราะห์ PDF
239
+ pdf_fields = analyze_pdf_fields(pdf_file)
240
+ has_form_fields = bool(pdf_fields and "error" not in pdf_fields and pdf_fields)
241
+
242
+ # เก็บ PDF ที่สร้าง
243
+ generated_pdfs = {}
244
+ success_count = 0
245
+ error_count = 0
246
+ processing_log = []
247
+
248
+ # ประมวลผลแต่ละแถว
249
+ for index, (_, row) in enumerate(df.iterrows()):
250
+ progress((index + 1) / len(df), f"ประมวลผล {index + 1}/{len(df)}")
251
+
252
+ try:
253
+ # สร้างชื่อไฟล์
254
+ if filename_column and filename_column in df.columns and pd.notna(row[filename_column]):
255
+ safe_name = str(row[filename_column]).strip()
256
+ safe_name = "".join(c for c in safe_name if c.isalnum() or c in (' ', '-', '_')).strip()
257
+ filename = f"{file_prefix}_{safe_name}.pdf"
258
+ else:
259
+ filename = f"{file_prefix}_{index + 1:03d}.pdf"
260
+
261
+ filename = filename.replace(' ', ' ').replace(' ', '_')
262
+ if not filename.endswith('.pdf'):
263
+ filename += '.pdf'
264
+
265
+ # ประมวลผล
266
+ pdf_content, status = process_single_row(
267
+ pdf_file,
268
+ row,
269
+ filename,
270
+ use_form_fields and has_form_fields
271
+ )
272
+
273
+ if pdf_content is not None:
274
+ generated_pdfs[filename] = pdf_content
275
+ success_count += 1
276
+ processing_log.append(f"✅ {filename}: {status}")
277
+ else:
278
+ error_count += 1
279
+ processing_log.append(f"❌ {filename}: {status}")
280
+
281
+ except Exception as e:
282
+ error_count += 1
283
+ processing_log.append(f"💥 แถว {index + 1}: {str(e)}")
284
+
285
+ # สร้าง ZIP
286
+ if generated_pdfs:
287
+ zip_buffer = io.BytesIO()
288
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
289
+ for filename, pdf_content in generated_pdfs.items():
290
+ zip_file.writestr(filename, pdf_content)
291
+
292
+ zip_buffer.seek(0)
293
+
294
+ # สร้างชื่อไฟล์ ZIP
295
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
296
+ zip_filename = f"generated_pdfs_{timestamp}.zip"
297
+
298
+ # บันทึกไฟล์ชั่วคราว
299
+ temp_zip_path = os.path.join(tempfile.gettempdir(), zip_filename)
300
+ with open(temp_zip_path, 'wb') as f:
301
+ f.write(zip_buffer.getvalue())
302
+
303
+ result_message = f"✅ สร้าง PDF สำเร็จ {success_count} ไฟล์!"
304
+ if error_count > 0:
305
+ result_message += f"\n⚠️ มีข้อผิดพลาด {error_count} ไฟล์"
306
+
307
+ result_message += f"\n\n📋 รายละเอียด:\n" + "\n".join(processing_log[:10])
308
+ if len(processing_log) > 10:
309
+ result_message += f"\n... และอีก {len(processing_log) - 10} รายการ"
310
+
311
+ return temp_zip_path, result_message
312
+ else:
313
+ return None, "❌ ไม่สามารถสร้าง PDF ได้เลย"
314
+
315
+ except Exception as e:
316
+ return None, f"❌ เกิดข้อผิดพลาด: {str(e)}\n{traceback.format_exc()}"
317
+
318
+ def analyze_pdf_info(pdf_file):
319
+ """วิเคราะห์ข้อมูล PDF"""
320
+ if pdf_file is None:
321
+ return "ไม่มีไฟล์ PDF"
322
+
323
+ try:
324
+ reader = PdfReader(pdf_file)
325
+ info = f"📄 **ข้อมูล PDF:**\n"
326
+ info += f"- จำนวนหน้า: {len(reader.pages)}\n"
327
+
328
+ # ตรวจสอบฟิลด์
329
+ pdf_fields = analyze_pdf_fields(pdf_file)
330
+
331
+ if pdf_fields and "error" not in pdf_fields and pdf_fields:
332
+ info += f"- จำนวน Form Fields: {len(pdf_fields)}\n"
333
+ info += f"\n🏷️ **รายชื่อ Fields:**\n"
334
+ for name, details in list(pdf_fields.items())[:10]: # แสดงแค่ 10 ตัวแรก
335
+ info += f" - {name} ({details.get('type', 'Unknown')})\n"
336
+ if len(pdf_fields) > 10:
337
+ info += f" - ... และอีก {len(pdf_fields) - 10} fields\n"
338
+ else:
339
+ info += "- Form Fields: ไม่พบหรือไม่สามารถอ่านได้\n"
340
+ info += "- หมายเหตุ: จะสร้าง PDF ใหม่แทน\n"
341
+
342
+ return info
343
+ except Exception as e:
344
+ return f"❌ ไม่สามารถวิเคราะห์ PDF ได้: {str(e)}"
345
+
346
+ def analyze_csv_info(csv_file):
347
+ """วิเคราะห์ข้อมูล CSV"""
348
+ if csv_file is None:
349
+ return "ไม่มีไฟล์ CSV"
350
+
351
+ try:
352
+ df, error = read_csv_safe(csv_file)
353
+ if df is None:
354
+ return f"❌ ไม่สามารถอ่าน CSV ได้: {error}"
355
+
356
+ info = f"📋 **ข้อมูล CSV:**\n"
357
+ info += f"- จำนวนแถว: {len(df)}\n"
358
+ info += f"- จำนวนคอลัมน์: {len(df.columns)}\n"
359
+ info += f"\n📝 **รายชื่อคอลัมน์:**\n"
360
+
361
+ for col in df.columns[:15]: # แสดงแค่ 15 คอลัมน์แรก
362
+ info += f" - {col}\n"
363
+ if len(df.columns) > 15:
364
+ info += f" - ... และอีก {len(df.columns) - 15} คอลัมน์\n"
365
+
366
+ # ตรวจสอบข้อมูลที่ขาด
367
+ missing_data = df.isnull().sum()
368
+ if missing_data.any():
369
+ missing_cols = missing_data[missing_data > 0]
370
+ if len(missing_cols) > 0:
371
+ info += f"\n⚠️ **ข้อมูลที่ขาดหาย:**\n"
372
+ for col, count in missing_cols.head(5).items():
373
+ info += f" - {col}: {count} แถว\n"
374
+
375
+ return info
376
+ except Exception as e:
377
+ return f"❌ ไม่สามารถวิเคราะห์ CSV ได้: {str(e)}"
378
+
379
+ # สร้าง Gradio Interface
380
+ def create_interface():
381
+ with gr.Blocks(title="PDF Form Filler", theme=gr.themes.Soft()) as app:
382
+ gr.Markdown("""
383
+ # 📄 เครื่องมือเติมข้อมูล PDF จาก CSV
384
+
385
+ **เครื่องมือนี้สามารถ:**
386
+ - เติมข้อมูลลงในฟอร์ม PDF ที่มี form fields
387
+ - สร้าง PDF ใหม่หากไม่มี form fields หรือเติมไม่ได้
388
+ - รองรับ CSV หลาย encoding (UTF-8, TIS-620, CP874, etc.)
389
+ - ส่งออกเป็นไฟล์ ZIP
390
+ """)
391
+
392
+ with gr.Row():
393
+ with gr.Column(scale=1):
394
+ gr.Markdown("## 📁 อัพโหลดไฟล์")
395
+
396
+ pdf_file = gr.File(
397
+ label="PDF Template",
398
+ file_types=[".pdf"],
399
+ type="filepath"
400
+ )
401
+
402
+ csv_file = gr.File(
403
+ label="CSV Data",
404
+ file_types=[".csv"],
405
+ type="filepath"
406
+ )
407
+
408
+ gr.Markdown("## ⚙️ ตั้งค่า")
409
+
410
+ filename_column = gr.Textbox(
411
+ label="คอลัมน์สำหรับชื่อไฟล์ (ถ้ามี)",
412
+ placeholder="เช่น name, id, etc.",
413
+ value=""
414
+ )
415
+
416
+ file_prefix = gr.Textbox(
417
+ label="คำนำหน้าชื่อไฟล์",
418
+ value="document"
419
+ )
420
+
421
+ use_form_fields = gr.Checkbox(
422
+ label="ใช้ Form Fields (ถ้าพบ)",
423
+ value=True
424
+ )
425
+
426
+ process_btn = gr.Button(
427
+ "🚀 สร้าง PDF ทั้งหมด",
428
+ variant="primary",
429
+ size="lg"
430
+ )
431
+
432
+ with gr.Column(scale=2):
433
+ gr.Markdown("## 📊 ข้อมูลไฟล์")
434
+
435
+ pdf_info = gr.Markdown("ยังไม่มีไฟล์ PDF")
436
+ csv_info = gr.Markdown("ยังไม่มีไฟล์ CSV")
437
+
438
+ gr.Markdown("## 📥 ผลลัพธ์")
439
+
440
+ result_file = gr.File(
441
+ label="ไฟล์ ZIP ที่สร้าง",
442
+ visible=False
443
+ )
444
+
445
+ result_message = gr.Markdown("")
446
+
447
+ # Event handlers
448
+ pdf_file.change(
449
+ fn=analyze_pdf_info,
450
+ inputs=[pdf_file],
451
+ outputs=[pdf_info]
452
+ )
453
+
454
+ csv_file.change(
455
+ fn=analyze_csv_info,
456
+ inputs=[csv_file],
457
+ outputs=[csv_info]
458
+ )
459
+
460
+ process_btn.click(
461
+ fn=process_pdf_csv,
462
+ inputs=[
463
+ pdf_file,
464
+ csv_file,
465
+ filename_column,
466
+ file_prefix,
467
+ use_form_fields
468
+ ],
469
+ outputs=[result_file, result_message]
470
+ ).then(
471
+ fn=lambda x: gr.update(visible=x is not None),
472
+ inputs=[result_file],
473
+ outputs=[result_file]
474
+ )
475
+
476
+ return app
477
+
478
+ # รันแอป
479
+ if __name__ == "__main__":
480
+ app = create_interface()
481
+ app.launch(
482
+ server_name="0.0.0.0",
483
+ server_port=7860,
484
+ share=True, # สร้าง public URL
485
+ debug=True
486
+ )