AkashKumarave commited on
Commit
76f26db
·
verified ·
1 Parent(s): 2c0c380

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -99
app.py CHANGED
@@ -1,111 +1,179 @@
1
- from fastapi import FastAPI, File, UploadFile, HTTPException
2
- from fastapi.middleware.cors import CORSMiddleware
3
- from fastapi.responses import JSONResponse
4
- import pdfplumber
5
- from io import BytesIO
6
- import base64
7
- import uvicorn
8
- import logging
9
-
10
- # Set up logging
11
- logging.basicConfig(level=logging.INFO)
12
- logger = logging.getLogger(__name__)
13
-
14
- app = FastAPI()
15
 
16
- # Add CORS middleware
17
- app.add_middleware(
18
- CORSMiddleware,
19
- allow_origins=["*"],
20
- allow_credentials=True,
21
- allow_methods=["*"],
22
- allow_headers=["*"],
23
- )
 
 
24
 
25
- # Root route
26
- @app.get("/")
27
- async def root():
28
- return {"message": "PDF to Figma API"}
 
29
 
30
- @app.post("/api/convert")
31
- async def convert_pdf(file: UploadFile = File(...)):
32
  try:
33
- logger.info("Received file: %s", file.filename)
 
 
 
 
 
 
 
 
 
34
 
35
- # Read the uploaded file
36
- contents = await file.read()
37
- if not contents:
38
- logger.error("Empty file uploaded")
39
- raise HTTPException(status_code=400, detail="Empty file uploaded")
 
 
 
 
 
 
40
 
41
- # Parse PDF with pdfplumber
42
- with pdfplumber.open(BytesIO(contents)) as pdf:
43
- if not pdf.pages:
44
- logger.error("No pages found in PDF")
45
- raise HTTPException(status_code=400, detail="No pages found in PDF")
46
-
47
- page = pdf.pages[0] # First page
48
- width, height = page.width, page.height
49
- logger.info("Processing PDF page: width=%s, height=%s", width, height)
50
 
51
- # Initialize result
52
- result = {
53
- "width": width,
54
- "height": height,
55
- "texts": [],
56
- "images": [],
57
- "shapes": []
58
- }
 
59
 
60
- # Extract text
61
- for char in page.chars:
62
- result["texts"].append({
63
- "content": char["text"],
64
- "x": char["x0"],
65
- "y": char["y0"],
66
- "font_family": char["fontname"].split("+")[-1] or "Arial",
67
- "font_style": "Regular",
68
- "font_size": char["size"],
69
- "color": {"r": 0, "g": 0, "b": 0}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
- # Extract images
73
- for img in page.images:
74
- try:
75
- img_data = img["stream"].get_data()
76
- result["images"].append({
77
- "data": list(img_data),
78
- "x": img["x0"],
79
- "y": img["y0"],
80
- "width": img["width"],
81
- "height": img["height"]
82
- })
83
- except Exception as e:
84
- logger.warning("Failed to extract image: %s", str(e))
85
- continue
86
-
87
- # Extract shapes
88
- for curve in page.curves:
89
- try:
90
- path = " ".join([f"M {p['x']},{p['y']}" for p in curve["points"]])
91
- result["shapes"].append({
92
- "path": path,
93
- "x": curve["x0"],
94
- "y": curve["y0"],
95
- "color": {"r": 0, "g": 0, "b": 0}
96
- })
97
- except Exception as e:
98
- logger.warning("Failed to extract shape: %s", str(e))
99
- continue
100
 
101
- logger.info("PDF processing complete")
102
- return JSONResponse(content=result)
103
- except Exception as e:
104
- logger.error("Failed to process PDF: %s", str(e))
105
- raise HTTPException(status_code=500, detail=f"Failed to process PDF: {str(e)}")
106
- finally:
107
- await file.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- # Run uvicorn server
110
- if __name__ == "__main__":
111
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
 
2
+ import gradio as gr
3
+ import requests
4
+ from bs4 import BeautifulSoup
5
+ import json
6
+ import base64
7
+ from PIL import Image
8
+ import io
9
+ import os
10
+ from urllib.parse import urlparse
11
+ import re
12
 
13
+ def clean_url(url):
14
+ """Ensure URL has proper protocol"""
15
+ if not url.startswith(('http://', 'https://')):
16
+ url = 'https://' + url
17
+ return url
18
 
19
+ def get_page_screenshot(url):
20
+ """Get screenshot of the webpage using a screenshot API"""
21
  try:
22
+ # Using a free screenshot API (limited, for demo purposes)
23
+ api_url = f"https://api.apiflash.com/v1/urltoimage?access_key=demo&url={url}&format=jpeg&quality=80"
24
+ response = requests.get(api_url)
25
+ if response.status_code == 200:
26
+ return response.content
27
+ else:
28
+ return None
29
+ except Exception as e:
30
+ print(f"Screenshot error: {str(e)}")
31
+ return None
32
 
33
+ def extract_styles(element):
34
+ """Extract CSS styles from an element"""
35
+ styles = {}
36
+ if element.has_attr('style'):
37
+ style_text = element.get('style')
38
+ style_pairs = [s.strip() for s in style_text.split(';') if s.strip()]
39
+ for pair in style_pairs:
40
+ if ':' in pair:
41
+ prop, val = pair.split(':', 1)
42
+ styles[prop.strip()] = val.strip()
43
+ return styles
44
 
45
+ def extract_colors(styles):
46
+ """Extract colors from CSS styles"""
47
+ colors = []
48
+ color_props = ['color', 'background-color', 'border-color']
49
+ for prop in color_props:
50
+ if prop in styles and styles[prop] not in ['transparent', 'inherit', 'initial']:
51
+ colors.append(styles[prop])
52
+ return colors
 
53
 
54
+ def extract_fonts(styles):
55
+ """Extract fonts from CSS styles"""
56
+ fonts = []
57
+ if 'font-family' in styles:
58
+ font_str = styles['font-family']
59
+ # Split by comma and clean up quotes
60
+ font_list = [f.strip().strip('"\'') for f in font_str.split(',')]
61
+ fonts.extend(font_list)
62
+ return fonts
63
 
64
+ def process_website(url):
65
+ """Process a website and extract design elements"""
66
+ url = clean_url(url)
67
+
68
+ try:
69
+ # Fetch the webpage
70
+ headers = {
71
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
72
+ }
73
+ response = requests.get(url, headers=headers, timeout=15)
74
+ if response.status_code != 200:
75
+ return {"error": f"Failed to fetch website (Status code: {response.status_code})"}
76
+
77
+ # Parse HTML
78
+ soup = BeautifulSoup(response.text, 'html.parser')
79
+
80
+ # Get screenshot
81
+ screenshot = get_page_screenshot(url)
82
+ screenshot_base64 = None
83
+ if screenshot:
84
+ screenshot_base64 = base64.b64encode(screenshot).decode('utf-8')
85
+
86
+ # Extract text content
87
+ text_elements = []
88
+ for text_tag in soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'a', 'button']):
89
+ if text_tag.text.strip():
90
+ text_elements.append({
91
+ "tag": text_tag.name,
92
+ "text": text_tag.text.strip(),
93
+ "styles": extract_styles(text_tag)
94
  })
95
+
96
+ # Extract colors
97
+ all_colors = []
98
+ for tag in soup.find_all(True):
99
+ styles = extract_styles(tag)
100
+ colors = extract_colors(styles)
101
+ all_colors.extend(colors)
102
+
103
+ # Extract fonts
104
+ all_fonts = []
105
+ for tag in soup.find_all(True):
106
+ styles = extract_styles(tag)
107
+ fonts = extract_fonts(styles)
108
+ all_fonts.extend(fonts)
109
+
110
+ # Extract images
111
+ images = []
112
+ for img in soup.find_all('img'):
113
+ if img.has_attr('src'):
114
+ src = img['src']
115
+ if src.startswith('//'):
116
+ src = 'https:' + src
117
+ elif not src.startswith(('http://', 'https://')):
118
+ # Handle relative URLs
119
+ base_url = '{uri.scheme}://{uri.netloc}'.format(uri=urlparse(url))
120
+ if src.startswith('/'):
121
+ src = base_url + src
122
+ else:
123
+ src = base_url + '/' + src
124
+
125
+ images.append({
126
+ "src": src,
127
+ "alt": img.get('alt', ''),
128
+ "width": img.get('width', ''),
129
+ "height": img.get('height', '')
130
+ })
131
+
132
+ # Create result
133
+ result = {
134
+ "url": url,
135
+ "title": soup.title.string if soup.title else "",
136
+ "screenshot": screenshot_base64,
137
+ "text_elements": text_elements[:50], # Limit to 50 elements
138
+ "colors": list(set(all_colors))[:20], # Remove duplicates and limit
139
+ "fonts": list(set(all_fonts))[:10], # Remove duplicates and limit
140
+ "images": images[:20] # Limit to 20 images
141
+ }
142
+
143
+ return result
144
+
145
+ except Exception as e:
146
+ return {"error": str(e)}
147
 
148
+ def figma_conversion_api(url):
149
+ """API endpoint for Figma plugin to convert websites"""
150
+ if not url:
151
+ return {"error": "No URL provided"}
152
+
153
+ result = process_website(url)
154
+ return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
+ # Create Gradio interface
157
+ with gr.Blocks() as demo:
158
+ gr.Markdown("# Website to Figma Converter API")
159
+
160
+ with gr.Tab("API Endpoint"):
161
+ gr.Markdown("""
162
+ ## API Usage
163
+ This API provides website conversion functionality for the Figma plugin.
164
+
165
+ ### Example usage:
166
+ Send a POST request with a URL parameter to convert a website to Figma-compatible format.
167
+ """)
168
+
169
+ with gr.Tab("Test Interface"):
170
+ with gr.Row():
171
+ url_input = gr.Textbox(label="Enter Website URL", placeholder="https://example.com")
172
+ convert_button = gr.Button("Convert Website")
173
+
174
+ output = gr.JSON(label="Conversion Result")
175
+
176
+ convert_button.click(figma_conversion_api, inputs=url_input, outputs=output)
177
 
178
+ # Mount the Gradio app
179
+ demo.launch()