File size: 8,860 Bytes
0002c06
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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.")