File size: 4,460 Bytes
fd90193
c58c1d8
 
fd90193
 
79697ac
fd90193
 
1f99de8
fd90193
79697ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04f69e7
c58c1d8
79697ac
fd90193
c58c1d8
c325e25
fd90193
c58c1d8
c325e25
c58c1d8
 
04f69e7
c58c1d8
04f69e7
 
 
 
c58c1d8
79697ac
04f69e7
 
c325e25
 
 
 
04f69e7
c325e25
79697ac
 
 
04f69e7
c325e25
 
 
 
79697ac
 
c325e25
 
79697ac
 
c325e25
 
 
79697ac
 
 
 
 
 
 
 
 
 
c325e25
79697ac
 
 
 
 
 
 
 
 
 
 
 
c325e25
 
79697ac
04f69e7
79697ac
 
04f69e7
c325e25
 
 
 
 
 
1f99de8
04f69e7
 
 
fd90193
1f99de8
fd90193
 
c58c1d8
 
fd90193
 
c58c1d8
 
fd90193
 
 
c58c1d8
 
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
import io
import os
import tempfile
from numbers_parser import Document
from openpyxl import Workbook
from openpyxl.styles import PatternFill, Font, Alignment
import gradio as gr
import pandas as pd
from pathlib import Path

def get_excel_color(numbers_color):
    """Convert Numbers color to Excel color format"""
    if not numbers_color:
        return None
    # Convert RGB values to hex color
    r = int(numbers_color.red * 255)
    g = int(numbers_color.green * 255)
    b = int(numbers_color.blue * 255)
    return f"{r:02x}{g:02x}{b:02x}"

def apply_cell_formatting(cell, cell_data):
    """Apply formatting from Numbers cell to Excel cell"""
    if hasattr(cell_data, 'background_color') and cell_data.background_color:
        color = get_excel_color(cell_data.background_color)
        if color:
            cell.fill = PatternFill(start_color=color, end_color=color, fill_type="solid")
    
    if hasattr(cell_data, 'font_bold'):
        cell.font = Font(bold=cell_data.font_bold)

def numbers_to_xlsx(numbers_file):
    """
    Converts a Numbers file to XLSX format preserving colors and formatting.

    Args:
        numbers_file: The uploaded Numbers file object from Gradio.

    Returns:
        str: Path to the converted xlsx file, or None if conversion fails.
    """
    if not numbers_file:
        return None
    
    try:
        output_dir = "outputs"
        os.makedirs(output_dir, exist_ok=True)
        output_path = os.path.join(output_dir, "converted.xlsx")
        
        # Read the Numbers file
        doc = Document(numbers_file.name)
        
        # Get all sheets and validate
        sheets = doc.sheets
        if not sheets:
            print("No sheets found in the document")
            return None
            
        # Create a new workbook
        wb = Workbook()
        wb.remove(wb.active)  # Remove default sheet
        
        for sheet_index, sheet in enumerate(sheets):
            if not sheet.tables:
                continue
                
            # Create new sheet
            ws = wb.create_sheet(title=f"Sheet{sheet_index + 1}")
            table = sheet.tables[0]
            
            # Get all rows with their formatting
            rows = list(table.rows())
            if not rows:
                continue
            
            # Write data and apply formatting
            for row_idx, row in enumerate(rows, 1):
                for col_idx, cell_data in enumerate(row, 1):
                    # Write cell value
                    cell = ws.cell(row=row_idx, column=col_idx)
                    cell.value = cell_data.value if hasattr(cell_data, 'value') else cell_data
                    
                    # Apply formatting if available
                    if hasattr(cell_data, 'background_color') or hasattr(cell_data, 'font_bold'):
                        apply_cell_formatting(cell, cell_data)
            
            # Adjust column widths
            for column in ws.columns:
                max_length = 0
                column_letter = column[0].column_letter
                for cell in column:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                adjusted_width = (max_length + 2)
                ws.column_dimensions[column_letter].width = adjusted_width
            
            # Freeze the header row
            ws.freeze_panes = 'A2'
        
        # Save the workbook
        wb.save(output_path)
        
        # Verify the file was created and has content
        if os.path.exists(output_path) and os.path.getsize(output_path) > 0:
            return output_path
        else:
            print("Output file is empty or was not created")
            return None
            
    except Exception as e:
        print(f"Error converting file: {str(e)}")
        return None

# Define the Gradio interface with correct file handling
interface = gr.Interface(
    fn=numbers_to_xlsx,
    inputs=gr.File(label="Numbers File", file_types=[".numbers"]),
    outputs=gr.File(label="XLSX file", file_types=[".xlsx"]),
    title="Numbers to XLSX Converter",
    description="Convert your Numbers files to Excel format easily and download the result.",
    examples=None,
    cache_examples=False
)

# Launch the Gradio app
if __name__ == "__main__":
    interface.launch()