aakanksha77 commited on
Commit
f52d4df
·
verified ·
1 Parent(s): 002d991

Update pdf_section_extractor.py

Browse files
Files changed (1) hide show
  1. pdf_section_extractor.py +493 -155
pdf_section_extractor.py CHANGED
@@ -1,6 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import pdfplumber
2
  import pandas as pd
3
  import re
 
4
  from typing import List, Dict, Tuple, Any
5
 
6
  class PDFSectionExtractor:
@@ -8,8 +225,63 @@ class PDFSectionExtractor:
8
  """Initialize with path to PDF file."""
9
  self.pdf_path = pdf_path
10
  self.tables = []
11
- self.table_names = {} # Store table names and their content
12
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  def is_level_three_section(self, section_number: str) -> bool:
14
  """Check if the section number is a level three section (e.g., 2.1.1)."""
15
  return len(section_number.split('.')) == 3
@@ -20,77 +292,139 @@ class PDFSectionExtractor:
20
  return [(match.group(1), match.group(2).strip())
21
  for match in re.finditer(table_pattern, text)]
22
 
23
- def clean_content(self, content: str) -> str:
24
- """Remove table references and names from content."""
25
- # Remove table references
26
- content = re.sub(r'\*\*Table\s+\d+:\s+[^*]+\*\*', '', content)
27
- # Remove any empty lines created
28
- content = '\n'.join(line for line in content.split('\n') if line.strip())
29
- return content
30
-
31
- def merge_split_tables(self, tables: List[List]) -> List[List]:
32
- """Merge tables that are split across pages."""
33
- merged_tables = []
34
- current_table = None
35
-
36
- for table in tables:
37
- if not table:
38
- continue
39
-
40
- if current_table is None:
41
- current_table = table
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  else:
43
- # Check if this table is a continuation
44
- # Compare the number of columns
45
- if len(table[0]) == len(current_table[0]):
46
- current_table.extend(table)
47
- else:
48
- merged_tables.append(current_table)
49
- current_table = table
50
-
51
- if current_table:
52
- merged_tables.append(current_table)
53
-
54
- return merged_tables
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- def extract_tables(self) -> List[Dict[str, Any]]:
57
- """Extract tables with their names and merge split tables."""
58
- tables_data = []
59
- current_section = None
60
- current_table_data = None
61
- current_table_name = None
62
-
63
  with pdfplumber.open(self.pdf_path) as pdf:
64
  for page in pdf.pages:
65
- text = page.extract_text(x_tolerance=1,y_tolerance=0) or ''
66
-
67
- # Find table names in the text
68
- table_names = self.find_table_names(text)
69
- tables = page.extract_tables()
70
-
71
- # Process each table found
72
- if tables:
73
- tables = self.merge_split_tables(tables)
74
-
75
- for i, table in enumerate(tables):
76
- table_name = None
77
- if i < len(table_names):
78
- table_num, name = table_names[i]
79
- table_name = f"Table {table_num}: {name}"
80
-
81
- if table: # Check if table has content
82
- df = pd.DataFrame(table)
83
- # Clean the DataFrame
84
- df = df.dropna(how='all').dropna(axis=1, how='all')
85
- # Replace None with empty string
86
- df = df.fillna('')
87
-
88
- tables_data.append({
89
- 'name': table_name,
90
- 'data': df
91
- })
92
-
93
- return tables_data
94
 
95
  def extract_sections(self) -> List[Dict[str, str]]:
96
  """Extract sections from PDF with content, excluding tables."""
@@ -98,116 +432,120 @@ class PDFSectionExtractor:
98
  current_section = None
99
  current_content = []
100
  section_pattern = r'^(\d+\.(?:\d+)?(?:\.\d+)?)\s+(.+)$'
101
-
 
 
 
 
102
  with pdfplumber.open(self.pdf_path) as pdf:
103
  for page in pdf.pages:
104
  text = page.extract_text(x_tolerance=1)
105
  if not text:
106
  continue
107
-
108
  lines = text.split('\n')
109
-
 
110
  for line in lines:
111
- match = re.match(section_pattern, line.strip())
112
-
 
113
  if match:
114
  if current_section:
115
- content_text = '\n'.join(current_content)
116
- content_text = self.clean_content(content_text)
117
-
118
- if self.is_level_three_section(current_section[0]):
119
- full_content = current_section[1] + '\n' + content_text
120
- sections.append({
121
- 'section_number': current_section[0],
122
- 'section_name': '',
123
- 'content': full_content
124
- })
125
- else:
126
- sections.append({
127
- 'section_number': current_section[0],
128
- 'section_name': current_section[1],
129
- 'content': content_text
130
- })
131
-
132
  current_section = (match.group(1), match.group(2))
133
  current_content = []
134
  elif current_section:
135
- current_content.append(line.strip())
136
-
137
- # Handle the last section
 
 
 
 
 
 
138
  if current_section:
139
- content_text = '\n'.join(current_content)
140
- content_text = self.clean_content(content_text)
141
-
142
- if self.is_level_three_section(current_section[0]):
143
- full_content = current_section[1] + '\n' + content_text
144
- sections.append({
145
- 'section_number': current_section[0],
146
- 'section_name': '',
147
- 'content': full_content
148
- })
149
- else:
150
- sections.append({
151
- 'section_number': current_section[0],
152
- 'section_name': current_section[1],
153
- 'content': content_text
154
- })
155
-
156
  return sections
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  def convert_pdf_to_excel(pdf_path: str, excel_path: str):
159
- """Convert PDF with sections and tables to Excel file."""
160
  try:
161
  extractor = PDFSectionExtractor(pdf_path)
162
- sections = extractor.extract_sections()
 
163
  tables = extractor.extract_tables()
164
-
 
 
 
 
 
165
  with pd.ExcelWriter(excel_path, engine='openpyxl') as writer:
166
- # Write sections to main sheet
167
- df_sections = pd.DataFrame(sections)
168
  df_sections.to_excel(writer, index=False, sheet_name='Sections')
169
 
170
- # Auto-adjust sections sheet
171
- worksheet = writer.sheets['Sections']
172
- for idx, col in enumerate(['A', 'B', 'C']):
173
- worksheet.column_dimensions[col].width = 15 if idx < 2 else 50
174
-
175
- # Write tables to separate sheets
176
- for i, table_info in enumerate(tables, 1):
177
- if table_info['name']:
178
- sheet_name = table_info['name'][:31] # Excel sheet name length limit
179
- else:
180
- sheet_name = f'Table_{i}'
181
-
182
- # Write table data
183
- table_info['data'].to_excel(writer, sheet_name=sheet_name, index=False)
184
-
185
- # Auto-adjust table sheet
186
- worksheet = writer.sheets[sheet_name]
187
- for column in worksheet.columns:
188
- max_length = 0
189
- column = [cell for cell in column]
190
- for cell in column:
191
- try:
192
- if len(str(cell.value)) > max_length:
193
- max_length = len(cell.value)
194
- except:
195
- pass
196
- adjusted_width = (max_length + 2)
197
- worksheet.column_dimensions[column[0].column_letter].width = adjusted_width
198
 
199
- return True
200
-
201
  except Exception as e:
202
- print(f"Error converting PDF to Excel: {str(e)}")
203
- return False
204
 
205
  if __name__ == "__main__":
206
- pdf_path = "/Users/aakanksha.n/Desktop/pdf_to_excel/[00 12 10] 26251-100-3DR-S04-00001_002.docx.pdf"
207
- excel_path = "/Users/aakanksha.n/Desktop/pdf_to_excel/format4.xlsx"
208
 
209
- success = convert_pdf_to_excel(pdf_path, excel_path)
210
- if success:
211
- print("Successfully converted PDF to Excel!")
212
- else:
213
- print("Failed to convert PDF to Excel.")
 
1
+ # import pdfplumber
2
+ # import pandas as pd
3
+ # import re
4
+ # from typing import List, Dict, Tuple, Any
5
+
6
+ # class PDFSectionExtractor:
7
+ # def __init__(self, pdf_path: str):
8
+ # """Initialize with path to PDF file."""
9
+ # self.pdf_path = pdf_path
10
+ # self.tables = []
11
+ # self.table_names = {} # Store table names and their content
12
+
13
+ # def is_level_three_section(self, section_number: str) -> bool:
14
+ # """Check if the section number is a level three section (e.g., 2.1.1)."""
15
+ # return len(section_number.split('.')) == 3
16
+
17
+ # def find_table_names(self, text: str) -> List[Dict[str, str]]:
18
+ # """Extract table names from text."""
19
+ # table_pattern = r'\*\*Table\s+(\d+):\s+([^*]+)\*\*'
20
+ # return [(match.group(1), match.group(2).strip())
21
+ # for match in re.finditer(table_pattern, text)]
22
+
23
+ # def clean_content(self, content: str) -> str:
24
+ # """Remove table references and names from content."""
25
+ # # Remove table references
26
+ # content = re.sub(r'\*\*Table\s+\d+:\s+[^*]+\*\*', '', content)
27
+ # # Remove any empty lines created
28
+ # content = '\n'.join(line for line in content.split('\n') if line.strip())
29
+ # return content
30
+
31
+ # def merge_split_tables(self, tables: List[List]) -> List[List]:
32
+ # """Merge tables that are split across pages."""
33
+ # merged_tables = []
34
+ # current_table = None
35
+
36
+ # for table in tables:
37
+ # if not table:
38
+ # continue
39
+
40
+ # if current_table is None:
41
+ # current_table = table
42
+ # else:
43
+ # # Check if this table is a continuation
44
+ # # Compare the number of columns
45
+ # if len(table[0]) == len(current_table[0]):
46
+ # current_table.extend(table)
47
+ # else:
48
+ # merged_tables.append(current_table)
49
+ # current_table = table
50
+
51
+ # if current_table:
52
+ # merged_tables.append(current_table)
53
+
54
+ # return merged_tables
55
+
56
+ # def extract_tables(self) -> List[Dict[str, Any]]:
57
+ # """Extract tables with their names and merge split tables."""
58
+ # tables_data = []
59
+ # current_section = None
60
+ # current_table_data = None
61
+ # current_table_name = None
62
+
63
+ # with pdfplumber.open(self.pdf_path) as pdf:
64
+ # for page in pdf.pages:
65
+ # text = page.extract_text(x_tolerance=1,y_tolerance=0) or ''
66
+
67
+ # # Find table names in the text
68
+ # table_names = self.find_table_names(text)
69
+ # tables = page.extract_tables()
70
+
71
+ # # Process each table found
72
+ # if tables:
73
+ # tables = self.merge_split_tables(tables)
74
+
75
+ # for i, table in enumerate(tables):
76
+ # table_name = None
77
+ # if i < len(table_names):
78
+ # table_num, name = table_names[i]
79
+ # table_name = f"Table {table_num}: {name}"
80
+
81
+ # if table: # Check if table has content
82
+ # df = pd.DataFrame(table)
83
+ # # Clean the DataFrame
84
+ # df = df.dropna(how='all').dropna(axis=1, how='all')
85
+ # # Replace None with empty string
86
+ # df = df.fillna('')
87
+
88
+ # tables_data.append({
89
+ # 'name': table_name,
90
+ # 'data': df
91
+ # })
92
+
93
+ # return tables_data
94
+
95
+ # def extract_sections(self) -> List[Dict[str, str]]:
96
+ # """Extract sections from PDF with content, excluding tables."""
97
+ # sections = []
98
+ # current_section = None
99
+ # current_content = []
100
+ # section_pattern = r'^(\d+\.(?:\d+)?(?:\.\d+)?)\s+(.+)$'
101
+
102
+ # with pdfplumber.open(self.pdf_path) as pdf:
103
+ # for page in pdf.pages:
104
+ # text = page.extract_text(x_tolerance=1)
105
+ # if not text:
106
+ # continue
107
+
108
+ # lines = text.split('\n')
109
+
110
+ # for line in lines:
111
+ # match = re.match(section_pattern, line.strip())
112
+
113
+ # if match:
114
+ # if current_section:
115
+ # content_text = '\n'.join(current_content)
116
+ # content_text = self.clean_content(content_text)
117
+
118
+ # if self.is_level_three_section(current_section[0]):
119
+ # full_content = current_section[1] + '\n' + content_text
120
+ # sections.append({
121
+ # 'section_number': current_section[0],
122
+ # 'section_name': '',
123
+ # 'content': full_content
124
+ # })
125
+ # else:
126
+ # sections.append({
127
+ # 'section_number': current_section[0],
128
+ # 'section_name': current_section[1],
129
+ # 'content': content_text
130
+ # })
131
+
132
+ # current_section = (match.group(1), match.group(2))
133
+ # current_content = []
134
+ # elif current_section:
135
+ # current_content.append(line.strip())
136
+
137
+ # # Handle the last section
138
+ # if current_section:
139
+ # content_text = '\n'.join(current_content)
140
+ # content_text = self.clean_content(content_text)
141
+
142
+ # if self.is_level_three_section(current_section[0]):
143
+ # full_content = current_section[1] + '\n' + content_text
144
+ # sections.append({
145
+ # 'section_number': current_section[0],
146
+ # 'section_name': '',
147
+ # 'content': full_content
148
+ # })
149
+ # else:
150
+ # sections.append({
151
+ # 'section_number': current_section[0],
152
+ # 'section_name': current_section[1],
153
+ # 'content': content_text
154
+ # })
155
+
156
+ # return sections
157
+
158
+ # def convert_pdf_to_excel(pdf_path: str, excel_path: str):
159
+ # """Convert PDF with sections and tables to Excel file."""
160
+ # try:
161
+ # extractor = PDFSectionExtractor(pdf_path)
162
+ # sections = extractor.extract_sections()
163
+ # tables = extractor.extract_tables()
164
+
165
+ # with pd.ExcelWriter(excel_path, engine='openpyxl') as writer:
166
+ # # Write sections to main sheet
167
+ # df_sections = pd.DataFrame(sections)
168
+ # df_sections.to_excel(writer, index=False, sheet_name='Sections')
169
+
170
+ # # Auto-adjust sections sheet
171
+ # worksheet = writer.sheets['Sections']
172
+ # for idx, col in enumerate(['A', 'B', 'C']):
173
+ # worksheet.column_dimensions[col].width = 15 if idx < 2 else 50
174
+
175
+ # # Write tables to separate sheets
176
+ # for i, table_info in enumerate(tables, 1):
177
+ # if table_info['name']:
178
+ # sheet_name = table_info['name'][:31] # Excel sheet name length limit
179
+ # else:
180
+ # sheet_name = f'Table_{i}'
181
+
182
+ # # Write table data
183
+ # table_info['data'].to_excel(writer, sheet_name=sheet_name, index=False)
184
+
185
+ # # Auto-adjust table sheet
186
+ # worksheet = writer.sheets[sheet_name]
187
+ # for column in worksheet.columns:
188
+ # max_length = 0
189
+ # column = [cell for cell in column]
190
+ # for cell in column:
191
+ # try:
192
+ # if len(str(cell.value)) > max_length:
193
+ # max_length = len(cell.value)
194
+ # except:
195
+ # pass
196
+ # adjusted_width = (max_length + 2)
197
+ # worksheet.column_dimensions[column[0].column_letter].width = adjusted_width
198
+
199
+ # return True
200
+
201
+ # except Exception as e:
202
+ # print(f"Error converting PDF to Excel: {str(e)}")
203
+ # return False
204
+
205
+ # if __name__ == "__main__":
206
+ # pdf_path = "/Users/aakanksha.n/Desktop/pdf_to_excel/[00 12 10] 26251-100-3DR-S04-00001_002.docx.pdf"
207
+ # excel_path = "/Users/aakanksha.n/Desktop/pdf_to_excel/format4.xlsx"
208
+
209
+ # success = convert_pdf_to_excel(pdf_path, excel_path)
210
+ # if success:
211
+ # print("Successfully converted PDF to Excel!")
212
+ # else:
213
+ # print("Failed to convert PDF to Excel.")
214
+
215
+
216
+
217
  import pdfplumber
218
  import pandas as pd
219
  import re
220
+ import os
221
  from typing import List, Dict, Tuple, Any
222
 
223
  class PDFSectionExtractor:
 
225
  """Initialize with path to PDF file."""
226
  self.pdf_path = pdf_path
227
  self.tables = []
228
+ self.table_names = {}
229
+ self.table_content_markers = set()
230
+ self.table_content_lines = set()
231
+ self.table_headers = set()
232
+ self.document_name = None # Set to None to identify when it's missing
233
+ self.document_id = None
234
+
235
+ def extract_document_info_from_pdf(self) -> Tuple[str, str]:
236
+ """Extract document name and ID from the first page of PDF."""
237
+ with pdfplumber.open(self.pdf_path) as pdf:
238
+ if not pdf.pages:
239
+ return "", ""
240
+
241
+ first_page_text = pdf.pages[0].extract_text()
242
+
243
+ if not first_page_text:
244
+ return "", ""
245
+
246
+ doc_name_pattern = r'[dD]ocument\s*[nN]ame:\s*(.*?)(?:\n|$)'
247
+ doc_id_pattern = r'[dD]ocument\s*[iI][dD]:\s*(.*?)(?:\n|$)'
248
+
249
+
250
+ doc_name_match = re.search(doc_name_pattern, first_page_text)
251
+ doc_id_match = re.search(doc_id_pattern, first_page_text)
252
+ print(f" extract document info function{doc_name_match}")
253
+
254
+ doc_name = doc_name_match.group(1).strip() if doc_name_match else ""
255
+ doc_id = doc_id_match.group(1).strip() if doc_id_match else ""
256
+
257
+ # print(f"Document name from pdf inside function{doc_name},{doc_id}")
258
+
259
+ return doc_name, doc_id
260
+
261
+ def get_document_info(self) -> Tuple[str, str]:
262
+ """
263
+ Get document info from the PDF content.
264
+ If missing, fall back to filename for document ID only.
265
+ """
266
+ if self.document_name is None or self.document_id is None:
267
+ # Try extracting from PDF
268
+ doc_name, doc_id = self.extract_document_info_from_pdf()
269
+ self.document_name = doc_name
270
+ self.document_id = doc_id
271
+ # print(f"Entering if one,{self.document_name}")
272
+
273
+ # Fallback only if document ID or name is missing
274
+ if not self.document_name or not self.document_id:
275
+ doc_name = os.path.basename(self.pdf_path)
276
+ doc_id_match = re.search(r'\[\d{2}\s+\d{2}\s+\d{2}\]\s+(\d+)', doc_name)
277
+ if not self.document_id:
278
+ self.document_id = doc_id_match.group(1) if doc_id_match else ""
279
+ if not self.document_name:
280
+ self.document_name = doc_name
281
+
282
+ # print(f"final{self.document_name},{self.document_id}")
283
+ return self.document_name, self.document_id
284
+
285
  def is_level_three_section(self, section_number: str) -> bool:
286
  """Check if the section number is a level three section (e.g., 2.1.1)."""
287
  return len(section_number.split('.')) == 3
 
292
  return [(match.group(1), match.group(2).strip())
293
  for match in re.finditer(table_pattern, text)]
294
 
295
+ def store_table_headers(self, table: List[List[str]]):
296
+ """Store table headers."""
297
+ if table and table[0]:
298
+ header_row = table[0]
299
+ for header in header_row:
300
+ if header:
301
+ header_text = self.fix_table_cell_spacing(header)
302
+ if header_text:
303
+ self.table_headers.add(header_text)
304
+
305
+ def store_table_content(self, table: List[List[str]]):
306
+ """Store all content from table cells, including headers."""
307
+ if not table:
308
+ return
309
+
310
+ # Store headers separately
311
+ self.store_table_headers(table)
312
+
313
+ # Store all cell content
314
+ for row in table:
315
+ for cell in row:
316
+ if cell:
317
+ cell_text = self.fix_table_cell_spacing(cell)
318
+ if cell_text:
319
+ self.table_content_lines.add(cell_text)
320
+
321
+ def fix_table_cell_spacing(self, cell_text: str) -> str:
322
+ """Fix spacing issues within table cells."""
323
+ if not isinstance(cell_text, str):
324
+ return str(cell_text)
325
+
326
+ text = str(cell_text).strip()
327
+ text = re.sub(r',(?=\S)', ', ', text)
328
+ text = re.sub(r'(\d)([a-zA-Z])', r'\1 \2', text)
329
+ text = re.sub(r'([a-zA-Z])(\d)', r'\1 \2', text)
330
+ text = re.sub(r'([a-z])([A-Z])', r'\1 \2', text)
331
+ text = re.sub(r'\s+', ' ', text)
332
+ return text.strip()
333
+
334
+ def contains_table_content(self, line: str) -> bool:
335
+ """Check if a line contains any stored table content or headers."""
336
+ line = self.fix_table_cell_spacing(line)
337
+ return (any(table_line in line for table_line in self.table_content_lines) or
338
+ any(header in line for header in self.table_headers))
339
+
340
+ def is_table_content(self, line: str) -> bool:
341
+ """Identify if a line is part of a table."""
342
+ if any(marker in line for marker in ['|', '+', '─', '│', '��', '┐', '└', '┘', '├', '┤', '┬', '┴']):
343
+ return True
344
+ if re.search(r'\S+\s{2,}\S+', line):
345
+ return True
346
+ if re.match(r'^[\s\-+|=_]{3,}$', line):
347
+ return True
348
+ return False
349
+
350
+ def filter_table_content(self, content: str) -> str:
351
+ """Remove lines that contain table content."""
352
+ lines = content.split('\n')
353
+ filtered_lines = []
354
+
355
+ for line in lines:
356
+ line = line.strip()
357
+ if line and not self.contains_table_content(line):
358
+ filtered_lines.append(line)
359
+
360
+ return '\n'.join(filtered_lines)
361
+
362
+ def process_table(self, table: List[List[str]], table_num: int) -> pd.DataFrame:
363
+ """Process table data with proper spacing and ensure unique column names."""
364
+ if not table:
365
+ return pd.DataFrame()
366
+
367
+ # Store all table content including headers
368
+ self.store_table_content(table)
369
+
370
+ # Create DataFrame with default column names
371
+ df = pd.DataFrame(table)
372
+ if df.empty:
373
+ return df
374
+
375
+ # Get the header row (first row)
376
+ header = df.iloc[0].tolist()
377
+
378
+ # Create unique column names
379
+ unique_cols = []
380
+ col_count = {}
381
+
382
+ for col in header:
383
+ col = str(col) if col else "Unnamed"
384
+ if col in col_count:
385
+ col_count[col] += 1
386
+ unique_cols.append(f"{col}_{col_count[col]}")
387
  else:
388
+ col_count[col] = 0
389
+ unique_cols.append(col)
390
+
391
+ # Set the unique column names
392
+ df.columns = unique_cols
393
+
394
+ # Remove the header row since we used it for column names
395
+ df = df.iloc[1:].reset_index(drop=True)
396
+
397
+ # Fix spacing in cells
398
+ for col in df.columns:
399
+ df[col] = df[col].astype(str).apply(self.fix_table_cell_spacing)
400
+
401
+ # Add metadata
402
+ df.insert(0, 'Document_Name', self.document_name)
403
+ df.insert(1, 'Document_ID', self.document_id)
404
+ df.insert(2, 'Table_Number', table_num)
405
+
406
+ return df
407
+
408
+ def extract_tables(self) -> List[pd.DataFrame]:
409
+ """Extract tables from PDF and return a list of DataFrames."""
410
+ tables = []
411
+ table_counter = 1
412
+
413
+ # Ensure we have document info before processing tables
414
+ self.get_document_info()
415
 
 
 
 
 
 
 
 
416
  with pdfplumber.open(self.pdf_path) as pdf:
417
  for page in pdf.pages:
418
+ detected_tables = page.extract_tables()
419
+
420
+ for table in detected_tables:
421
+ if table:
422
+ df = self.process_table(table, table_counter)
423
+ if not df.empty:
424
+ tables.append(df)
425
+ table_counter += 1
426
+
427
+ return tables
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
 
429
  def extract_sections(self) -> List[Dict[str, str]]:
430
  """Extract sections from PDF with content, excluding tables."""
 
432
  current_section = None
433
  current_content = []
434
  section_pattern = r'^(\d+\.(?:\d+)?(?:\.\d+)?)\s+(.+)$'
435
+
436
+ # Ensure we have document info before processing sections
437
+ doc_name, doc_id = self.get_document_info()
438
+ table_counter = 1
439
+
440
  with pdfplumber.open(self.pdf_path) as pdf:
441
  for page in pdf.pages:
442
  text = page.extract_text(x_tolerance=1)
443
  if not text:
444
  continue
445
+
446
  lines = text.split('\n')
447
+ table_found = False
448
+
449
  for line in lines:
450
+ line = line.strip()
451
+ match = re.match(section_pattern, line)
452
+
453
  if match:
454
  if current_section:
455
+ content_text = self.process_content(current_content)
456
+ content_text = self.filter_table_content(content_text)
457
+ sections.append(self.create_section_dict(
458
+ doc_name, doc_id, current_section, content_text))
459
+
 
 
 
 
 
 
 
 
 
 
 
 
460
  current_section = (match.group(1), match.group(2))
461
  current_content = []
462
  elif current_section:
463
+ if self.is_table_content(line):
464
+ if not table_found:
465
+ current_content.append(f"[Table {table_counter} is available in Table_{table_counter} sheet]")
466
+ table_counter += 1
467
+ table_found = True
468
+ else:
469
+ current_content.append(line)
470
+ table_found = False
471
+
472
  if current_section:
473
+ content_text = self.process_content(current_content)
474
+ content_text = self.filter_table_content(content_text)
475
+ sections.append(self.create_section_dict(
476
+ doc_name, doc_id, current_section, content_text))
477
+
 
 
 
 
 
 
 
 
 
 
 
 
478
  return sections
479
 
480
+ def process_content(self, content_lines: List[str]) -> str:
481
+ """Process content lines and remove duplicate table references."""
482
+ processed_lines = []
483
+ last_line_was_table_ref = False
484
+
485
+ for line in content_lines: # Fixed: using content_lines parameter
486
+ line = line.strip()
487
+ if '[Table' in line:
488
+ if not last_line_was_table_ref:
489
+ processed_lines.append(line)
490
+ last_line_was_table_ref = True
491
+ else:
492
+ processed_lines.append(line)
493
+ last_line_was_table_ref = False
494
+
495
+ return '\n'.join(processed_lines)
496
+
497
+ def create_section_dict(self, doc_name: str, doc_id: str,
498
+ section_tuple: Tuple[str, str], content: str) -> Dict[str, str]:
499
+ """Create a dictionary for section data."""
500
+ section_number, section_name = section_tuple
501
+
502
+ if self.is_level_three_section(section_number):
503
+ return {
504
+ 'document_name': doc_name,
505
+ 'document_id': doc_id,
506
+ 'section_number': section_number,
507
+ 'section_name': '',
508
+ 'content': f"{section_name}\n{content}"
509
+ }
510
+ else:
511
+ return {
512
+ 'document_name': doc_name,
513
+ 'document_id': doc_id,
514
+ 'section_number': section_number,
515
+ 'section_name': section_name,
516
+ 'content': content
517
+ }
518
+
519
  def convert_pdf_to_excel(pdf_path: str, excel_path: str):
520
+ """Convert PDF with sections and tables to Excel file with separate sheets for each table."""
521
  try:
522
  extractor = PDFSectionExtractor(pdf_path)
523
+
524
+ # Extract and process tables
525
  tables = extractor.extract_tables()
526
+
527
+ # Extract sections
528
+ sections = extractor.extract_sections()
529
+ df_sections = pd.DataFrame(sections)
530
+
531
+ # Write to Excel file
532
  with pd.ExcelWriter(excel_path, engine='openpyxl') as writer:
533
+ # Write sections sheet
 
534
  df_sections.to_excel(writer, index=False, sheet_name='Sections')
535
 
536
+ # Write each table to its own sheet
537
+ for i, table_df in enumerate(tables, 1):
538
+ sheet_name = f'Table_{i}'
539
+ table_df.to_excel(writer, index=False, sheet_name=sheet_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
 
541
+ print(f"Successfully saved PDF data to {excel_path}")
542
+ print(f"Created {len(tables)} table sheets")
543
  except Exception as e:
544
+ print(f"Error occurred: {e}")
545
+ raise
546
 
547
  if __name__ == "__main__":
548
+ pdf_path = "[00 12 10] 26251-100-3DR-S04-00001_002.docx.pdf"
549
+ excel_path = "format.xlsx"
550
 
551
+ convert_pdf_to_excel(pdf_path, excel_path)