Spaces:
Sleeping
Sleeping
| import pdfplumber | |
| import pandas as pd | |
| import re | |
| from typing import List, Dict, Tuple, Any | |
| class PDFSectionExtractor: | |
| def __init__(self, pdf_path: str): | |
| """Initialize with path to PDF file.""" | |
| self.pdf_path = pdf_path | |
| self.tables = [] | |
| self.table_names = {} # Store table names and their content | |
| def is_level_three_section(self, section_number: str) -> bool: | |
| """Check if the section number is a level three section (e.g., 2.1.1).""" | |
| return len(section_number.split('.')) == 3 | |
| def find_table_names(self, text: str) -> List[Dict[str, str]]: | |
| """Extract table names from text.""" | |
| table_pattern = r'\*\*Table\s+(\d+):\s+([^*]+)\*\*' | |
| return [(match.group(1), match.group(2).strip()) | |
| for match in re.finditer(table_pattern, text)] | |
| def clean_content(self, content: str) -> str: | |
| """Remove table references and names from content.""" | |
| # Remove table references | |
| content = re.sub(r'\*\*Table\s+\d+:\s+[^*]+\*\*', '', content) | |
| # Remove any empty lines created | |
| content = '\n'.join(line for line in content.split('\n') if line.strip()) | |
| return content | |
| def merge_split_tables(self, tables: List[List]) -> List[List]: | |
| """Merge tables that are split across pages.""" | |
| merged_tables = [] | |
| current_table = None | |
| for table in tables: | |
| if not table: | |
| continue | |
| if current_table is None: | |
| current_table = table | |
| else: | |
| # Check if this table is a continuation | |
| # Compare the number of columns | |
| if len(table[0]) == len(current_table[0]): | |
| current_table.extend(table) | |
| else: | |
| merged_tables.append(current_table) | |
| current_table = table | |
| if current_table: | |
| merged_tables.append(current_table) | |
| return merged_tables | |
| def extract_tables(self) -> List[Dict[str, Any]]: | |
| """Extract tables with their names and merge split tables.""" | |
| tables_data = [] | |
| current_section = None | |
| current_table_data = None | |
| current_table_name = None | |
| with pdfplumber.open(self.pdf_path) as pdf: | |
| for page in pdf.pages: | |
| text = page.extract_text(x_tolerance=1,y_tolerance=0) or '' | |
| # Find table names in the text | |
| table_names = self.find_table_names(text) | |
| tables = page.extract_tables() | |
| # Process each table found | |
| if tables: | |
| tables = self.merge_split_tables(tables) | |
| for i, table in enumerate(tables): | |
| table_name = None | |
| if i < len(table_names): | |
| table_num, name = table_names[i] | |
| table_name = f"Table {table_num}: {name}" | |
| if table: # Check if table has content | |
| df = pd.DataFrame(table) | |
| # Clean the DataFrame | |
| df = df.dropna(how='all').dropna(axis=1, how='all') | |
| # Replace None with empty string | |
| df = df.fillna('') | |
| tables_data.append({ | |
| 'name': table_name, | |
| 'data': df | |
| }) | |
| return tables_data | |
| def extract_sections(self) -> List[Dict[str, str]]: | |
| """Extract sections from PDF with content, excluding tables.""" | |
| sections = [] | |
| current_section = None | |
| current_content = [] | |
| section_pattern = r'^(\d+\.(?:\d+)?(?:\.\d+)?)\s+(.+)$' | |
| with pdfplumber.open(self.pdf_path) as pdf: | |
| for page in pdf.pages: | |
| text = page.extract_text(x_tolerance=1) | |
| if not text: | |
| continue | |
| lines = text.split('\n') | |
| for line in lines: | |
| match = re.match(section_pattern, line.strip()) | |
| if match: | |
| if current_section: | |
| content_text = '\n'.join(current_content) | |
| content_text = self.clean_content(content_text) | |
| if self.is_level_three_section(current_section[0]): | |
| full_content = current_section[1] + '\n' + content_text | |
| sections.append({ | |
| 'section_number': current_section[0], | |
| 'section_name': '', | |
| 'content': full_content | |
| }) | |
| else: | |
| sections.append({ | |
| 'section_number': current_section[0], | |
| 'section_name': current_section[1], | |
| 'content': content_text | |
| }) | |
| current_section = (match.group(1), match.group(2)) | |
| current_content = [] | |
| elif current_section: | |
| current_content.append(line.strip()) | |
| # Handle the last section | |
| if current_section: | |
| content_text = '\n'.join(current_content) | |
| content_text = self.clean_content(content_text) | |
| if self.is_level_three_section(current_section[0]): | |
| full_content = current_section[1] + '\n' + content_text | |
| sections.append({ | |
| 'section_number': current_section[0], | |
| 'section_name': '', | |
| 'content': full_content | |
| }) | |
| else: | |
| sections.append({ | |
| 'section_number': current_section[0], | |
| 'section_name': current_section[1], | |
| 'content': content_text | |
| }) | |
| return sections | |
| def convert_pdf_to_excel(pdf_path: str, excel_path: str): | |
| """Convert PDF with sections and tables to Excel file.""" | |
| try: | |
| extractor = PDFSectionExtractor(pdf_path) | |
| sections = extractor.extract_sections() | |
| tables = extractor.extract_tables() | |
| with pd.ExcelWriter(excel_path, engine='openpyxl') as writer: | |
| # Write sections to main sheet | |
| df_sections = pd.DataFrame(sections) | |
| df_sections.to_excel(writer, index=False, sheet_name='Sections') | |
| # Auto-adjust sections sheet | |
| worksheet = writer.sheets['Sections'] | |
| for idx, col in enumerate(['A', 'B', 'C']): | |
| worksheet.column_dimensions[col].width = 15 if idx < 2 else 50 | |
| # Write tables to separate sheets | |
| for i, table_info in enumerate(tables, 1): | |
| if table_info['name']: | |
| sheet_name = table_info['name'][:31] # Excel sheet name length limit | |
| else: | |
| sheet_name = f'Table_{i}' | |
| # Write table data | |
| table_info['data'].to_excel(writer, sheet_name=sheet_name, index=False) | |
| # Auto-adjust table sheet | |
| worksheet = writer.sheets[sheet_name] | |
| for column in worksheet.columns: | |
| max_length = 0 | |
| column = [cell for cell in column] | |
| for cell in column: | |
| try: | |
| if len(str(cell.value)) > max_length: | |
| max_length = len(cell.value) | |
| except: | |
| pass | |
| adjusted_width = (max_length + 2) | |
| worksheet.column_dimensions[column[0].column_letter].width = adjusted_width | |
| return True | |
| except Exception as e: | |
| print(f"Error converting PDF to Excel: {str(e)}") | |
| return False | |
| if __name__ == "__main__": | |
| pdf_path = "/Users/aakanksha.n/Desktop/pdf_to_excel/[00 12 10] 26251-100-3DR-S04-00001_002.docx.pdf" | |
| excel_path = "/Users/aakanksha.n/Desktop/pdf_to_excel/format4.xlsx" | |
| success = convert_pdf_to_excel(pdf_path, excel_path) | |
| if success: | |
| print("Successfully converted PDF to Excel!") | |
| else: | |
| print("Failed to convert PDF to Excel.") |