tomo2chin2 commited on
Commit
4f4db76
·
verified ·
1 Parent(s): 71bb7c7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +308 -108
app.py CHANGED
@@ -1,131 +1,331 @@
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
2
 
3
- def display_html(html_code):
4
- return f"""
5
- <div style='overflow-x: hidden; height: 80vh; width:100%;'>
6
- {html_code}
7
- </div>
8
- """
9
-
10
- # (テーマとCSSは前の回答のものをそのまま使用)
11
- # ... (テーマ設定, css変数) ...
12
- # カスタムテーマ (フォント、テキストスタイルを大幅改良)
13
- theme = gr.themes.Default(
14
- primary_hue=gr.themes.colors.indigo,
15
- secondary_hue=gr.themes.colors.blue,
16
- neutral_hue=gr.themes.colors.gray,
17
- font=[gr.themes.GoogleFont('Roboto Serif'), 'ui-serif', 'serif'], # フォント変更
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  ).set(
19
- body_background_fill="linear-gradient(to bottom right, #f5f5f5, #e0e7ff)",
20
- body_text_color="rgba(0, 0, 0, 0.8)",
21
- body_text_size="md",
22
- body_text_color_subdued="rgba(0, 0, 0, 0.6)",
23
-
24
- block_background_fill="white",
25
- block_border_color="rgba(0, 0, 0, 0.1)",
26
- block_border_width="1px",
27
- block_title_text_weight="600",
28
- block_label_background_fill="rgba(0, 0, 0, 0.04)",
29
- block_label_text_color="primary",
30
- block_label_text_size="sm",
31
- block_label_margin="0.5rem",
32
- block_padding="2rem", #ブロック内のパディングを増やす
33
-
34
- button_primary_background_fill="primary",
35
- button_primary_background_fill_hover="dark",
36
  button_primary_text_color="white",
37
  button_secondary_background_fill="white",
38
- button_secondary_border_color="primary",
39
- button_secondary_text_color="primary",
40
-
41
- input_background_fill="#f9fafb",
42
- input_border_color="rgba(0, 0, 0, 0.2)",
43
- input_border_width="1px",
44
- input_text_size="md",
45
- input_placeholder_color="rgba(0, 0, 0, 0.4)",
46
-
47
- background_fill_secondary="white",
48
- border_color_accent="primary",
49
- border_color_primary="rgba(0, 0, 0, 0.2)",
50
  )
51
 
52
- # カスタム CSS (見出し、本文、ラベルのスタイル)
53
  css = """
54
- /* 見出し (h1) */
55
- #component-0 h1 { /* markdownコンポーネントは、自動でIDがつく*/
56
- font-size: 2.5rem; /* 大きめのフォントサイズ */
57
- font-weight: 700; /* 太字 */
58
- color: #312e81; /* 濃いインディゴ */
59
- margin-bottom: 1rem; /* 下部の余白 */
60
- text-align: center; /* 中央揃え */
61
  }
62
 
63
- /* 本文 (Markdown コンポーネント内の p タグ) */
64
- #component-0 p{
65
- font-size: 1.1rem; /* 適切なフォントサイズ */
66
- line-height: 1.6; /* 行間を広げる */
67
- color: #4b5563; /* 落ち着いたグレー */
68
- margin-bottom: 1rem; /* 段落間の余白 */
69
  }
70
 
71
- /* ラベル */
72
- .gr-block label span {
73
- font-weight: 500; /* 少し太字に */
74
- color: #4b5563; /* 落ち着いたグレー */
 
 
75
  }
76
 
77
- /* リンクのスタイル(必要なら) */
78
- a {
79
- color: var(--link-text-color);
80
- text-decoration: underline;
81
  }
82
- a:hover {
83
- color: var(--link-hover-color);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
  """
86
 
87
- with gr.Blocks(theme=theme, title="HTML Viewer", css=css) as demo:
88
- gr.Markdown(
89
- """
90
- # HTML Viewer
91
-
92
- 入力されたHTMLコードをプレビュー表示します。
93
- """)
94
- with gr.Row():
95
- with gr.Column(scale=1):
96
- input_textbox = gr.Textbox(
97
- label="HTML��ード",
98
- placeholder="ここにHTMLコードを貼り付けてください...",
99
- lines=10
100
- )
101
- submit_button = gr.Button("表示", variant="secondary") # スタイルを元に戻す
102
-
103
-
104
- with gr.Column(scale=3):
105
- output_html = gr.HTML(label="プレビュー")
106
-
107
- submit_button.click(
108
- display_html,
109
- inputs=input_textbox,
110
- outputs=output_html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  )
112
-
113
- #サンプル
114
- gr.Examples(
115
- [["<h1>Hello world</h1>"],["""
116
- <!DOCTYPE html>
117
- <html>
118
- <head>
119
- <title>サンプルページ</title>
120
- </head>
121
- <body>
122
- <h1 style='color:blue';>Hello world!</h1>
123
- <p>サンプル</p>
124
- </body>
125
- </html>"""]],
126
-
127
- inputs = input_textbox
128
  )
129
 
 
130
  if __name__ == "__main__":
131
  demo.launch()
 
1
  import gradio as gr
2
+ import os
3
+ import tempfile
4
+ import time
5
+ import base64
6
+ from selenium import webdriver
7
+ from selenium.webdriver.chrome.options import Options
8
+ from selenium.webdriver.chrome.service import Service
9
+ from webdriver_manager.chrome import ChromeDriverManager
10
+ from PIL import Image
11
+ import io
12
 
13
+ # Setup directories
14
+ TEMP_DIR = os.path.join(tempfile.gettempdir(), "html-screenshots")
15
+ os.makedirs(TEMP_DIR, exist_ok=True)
16
+
17
+ def setup_driver():
18
+ """Setup and return a headless Chrome driver"""
19
+ chrome_options = Options()
20
+ chrome_options.add_argument("--headless")
21
+ chrome_options.add_argument("--no-sandbox")
22
+ chrome_options.add_argument("--disable-dev-shm-usage")
23
+ chrome_options.add_argument("--disable-gpu")
24
+ chrome_options.add_argument("--window-size=1280,1024")
25
+
26
+ # Use ChromeDriverManager to automatically download the appropriate driver
27
+ service = Service(ChromeDriverManager().install())
28
+
29
+ driver = webdriver.Chrome(service=service, options=chrome_options)
30
+ return driver
31
+
32
+ def get_screenshot(html_code, width=1280, height=800, full_page=False, wait_time=1):
33
+ """Take a screenshot of the HTML using headless Chrome"""
34
+ if not html_code.strip():
35
+ return None, "Please enter some HTML code first."
36
+
37
+ # Create a temporary HTML file
38
+ timestamp = int(time.time())
39
+ html_file = os.path.join(TEMP_DIR, f"temp_{timestamp}.html")
40
+
41
+ try:
42
+ # Add doctype and viewport meta if not present
43
+ if "<!DOCTYPE" not in html_code:
44
+ html_code = f"""<!DOCTYPE html>
45
+ <html>
46
+ <head>
47
+ <meta charset="UTF-8">
48
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
49
+ <style>
50
+ body {{
51
+ font-family: 'Arial', sans-serif;
52
+ line-height: 1.6;
53
+ color: #333;
54
+ max-width: 100%;
55
+ }}
56
+ </style>
57
+ </head>
58
+ <body>
59
+ {html_code}
60
+ </body>
61
+ </html>"""
62
+
63
+ # Write HTML to temporary file
64
+ with open(html_file, "w", encoding="utf-8") as f:
65
+ f.write(html_code)
66
+
67
+ # Initialize the driver
68
+ driver = setup_driver()
69
+
70
+ try:
71
+ # Navigate to the HTML file
72
+ driver.get(f"file://{html_file}")
73
+
74
+ # Wait for any JavaScript to load
75
+ time.sleep(wait_time)
76
+
77
+ # Set window size
78
+ driver.set_window_size(width, height)
79
+
80
+ # Take screenshot
81
+ if full_page:
82
+ # Get the height of the page
83
+ height = driver.execute_script("return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);")
84
+ driver.set_window_size(width, height)
85
+
86
+ screenshot = driver.get_screenshot_as_png()
87
+
88
+ # Convert to PIL Image for processing
89
+ img = Image.open(io.BytesIO(screenshot))
90
+
91
+ # Create an in-memory buffer for the image
92
+ buffer = io.BytesIO()
93
+ img.save(buffer, format="PNG")
94
+ img_str = base64.b64encode(buffer.getvalue()).decode("utf-8")
95
+
96
+ return f"data:image/png;base64,{img_str}", None
97
+
98
+ finally:
99
+ driver.quit()
100
+
101
+ except Exception as e:
102
+ return None, f"Error taking screenshot: {str(e)}"
103
+ finally:
104
+ # Clean up temporary file
105
+ if os.path.exists(html_file):
106
+ try:
107
+ os.remove(html_file)
108
+ except:
109
+ pass
110
+
111
+ return None, "Failed to generate screenshot."
112
+
113
+ def render_screenshot(html_code, width, height, full_page, wait_time):
114
+ """Process HTML and return screenshot with potential error message"""
115
+ screenshot, error = get_screenshot(
116
+ html_code,
117
+ width=width,
118
+ height=height,
119
+ full_page=full_page,
120
+ wait_time=wait_time
121
+ )
122
+
123
+ if error:
124
+ return None, error
125
+
126
+ return screenshot, None
127
+
128
+ # Create elegant theme inspired by Intercontinental Hotel Ishigaki
129
+ theme = gr.themes.Soft(
130
+ primary_hue="indigo",
131
+ secondary_hue="blue",
132
+ neutral_hue="slate",
133
+ font=gr.themes.GoogleFont("Inter"),
134
+ font_mono=gr.themes.GoogleFont("IBM Plex Mono"),
135
  ).set(
136
+ button_primary_background_fill="*primary_500",
137
+ button_primary_background_fill_hover="*primary_600",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  button_primary_text_color="white",
139
  button_secondary_background_fill="white",
140
+ button_secondary_background_fill_hover="*neutral_100",
141
+ button_secondary_text_color="*neutral_800",
142
+ block_title_text_color="*primary_700",
143
+ block_label_text_color="*neutral_600",
144
+ background_fill_primary="white"
 
 
 
 
 
 
 
145
  )
146
 
147
+ # Custom CSS for elegant styling
148
  css = """
149
+ .gradio-container {
150
+ max-width: 1200px !important;
151
+ margin: 0 auto !important;
152
+ background-color: #ffffff !important;
153
+ box-shadow: 0 4px 30px rgba(0, 0, 0, 0.03) !important;
154
+ border-radius: 16px !important;
155
+ overflow: hidden !important;
156
  }
157
 
158
+ .header-container {
159
+ background: linear-gradient(135deg, #e0f2fe, #f0f9ff) !important;
160
+ padding: 2rem 1.5rem 1.5rem !important;
161
+ border-bottom: 1px solid rgba(0, 0, 0, 0.03) !important;
162
+ margin-bottom: 1.5rem !important;
 
163
  }
164
 
165
+ .header-title {
166
+ font-weight: 300 !important;
167
+ color: #1e40af !important;
168
+ font-size: 2rem !important;
169
+ margin: 0 !important;
170
+ letter-spacing: -0.02em !important;
171
  }
172
 
173
+ .header-subtitle {
174
+ color: #475569 !important;
175
+ font-weight: 400 !important;
176
+ margin-top: 0.5rem !important;
177
  }
178
+
179
+ .input-panel {
180
+ background-color: white !important;
181
+ border-radius: 10px !important;
182
+ padding: 0.5rem !important;
183
+ border: 1px solid #e2e8f0 !important;
184
+ }
185
+
186
+ .screenshot-container {
187
+ background-color: white !important;
188
+ border: 1px solid #e2e8f0 !important;
189
+ border-radius: 10px !important;
190
+ overflow: hidden !important;
191
+ display: flex !important;
192
+ flex-direction: column !important;
193
+ align-items: center !important;
194
+ justify-content: center !important;
195
+ min-height: 400px !important;
196
+ }
197
+
198
+ .screenshot-container img {
199
+ max-width: 100% !important;
200
+ height: auto !important;
201
+ object-fit: contain !important;
202
+ margin: 0 auto !important;
203
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05) !important;
204
+ }
205
+
206
+ .error-message {
207
+ color: #ef4444 !important;
208
+ background-color: #fef2f2 !important;
209
+ padding: 1rem !important;
210
+ border-radius: 8px !important;
211
+ margin-top: 1rem !important;
212
+ font-size: 0.9rem !important;
213
+ border-left: 3px solid #ef4444 !important;
214
+ }
215
+
216
+ .footer {
217
+ text-align: center !important;
218
+ padding: 1.5rem !important;
219
+ color: #94a3b8 !important;
220
+ font-size: 0.8rem !important;
221
+ border-top: 1px solid #f1f5f9 !important;
222
+ margin-top: 2rem !important;
223
+ }
224
+
225
+ /* Responsive adjustments */
226
+ @media (max-width: 768px) {
227
+ .header-title {
228
+ font-size: 1.5rem !important;
229
+ }
230
+
231
+ .two-column {
232
+ flex-direction: column !important;
233
+ }
234
  }
235
  """
236
 
237
+ # Create the Gradio interface
238
+ with gr.Blocks(theme=theme, css=css) as demo:
239
+ # Header
240
+ with gr.Column(elem_classes=["header-container"]):
241
+ gr.Markdown("# HTML Screenshot Generator", elem_classes=["header-title"])
242
+ gr.Markdown("Capture beautiful screenshots of your HTML using headless Chrome",
243
+ elem_classes=["header-subtitle"])
244
+
245
+ # State for storing error messages
246
+ error_message = gr.State("")
247
+
248
+ # Main content with responsive layout
249
+ with gr.Row(equal_height=True, elem_classes=["two-column"]) as layout_row:
250
+ # Input panel
251
+ with gr.Column(scale=1, elem_classes=["input-panel"]):
252
+ html_input = gr.Code(
253
+ label="HTML Code",
254
+ language="html",
255
+ value="<!-- Enter your HTML here -->\n<h1>Hello, World!</h1>\n<p>This is a sample HTML page.</p>",
256
+ lines=15,
257
+ elem_id="html-input"
258
+ )
259
+
260
+ with gr.Accordion("Screenshot Options", open=False):
261
+ with gr.Row():
262
+ width_input = gr.Slider(
263
+ minimum=320, maximum=2560, value=1280, step=10,
264
+ label="Width (px)"
265
+ )
266
+ height_input = gr.Slider(
267
+ minimum=240, maximum=1600, value=800, step=10,
268
+ label="Height (px)"
269
+ )
270
+
271
+ with gr.Row():
272
+ full_page = gr.Checkbox(
273
+ label="Capture Full Page Height",
274
+ value=False
275
+ )
276
+ wait_time = gr.Slider(
277
+ minimum=0.1, maximum=5.0, value=1.0, step=0.1,
278
+ label="Wait Time (seconds)"
279
+ )
280
+
281
+ with gr.Row():
282
+ clear_btn = gr.Button("Clear", variant="secondary")
283
+ screenshot_btn = gr.Button("Take Screenshot", variant="primary")
284
+
285
+ # Error message display
286
+ error_display = gr.Markdown(
287
+ visible=False,
288
+ elem_classes=["error-message"]
289
+ )
290
+
291
+ # Screenshot panel
292
+ with gr.Column(scale=1, elem_classes=["screenshot-container"]):
293
+ screenshot_output = gr.Image(
294
+ label="Screenshot Preview",
295
+ type="filepath",
296
+ height=600
297
+ )
298
+
299
+ download_btn = gr.Button("Download Screenshot", visible=False)
300
+
301
+ # Footer
302
+ with gr.Column(elem_classes=["footer"]):
303
+ gr.Markdown("*Designed with the elegant simplicity of Intercontinental Hotel Ishigaki's Club Lounge in mind*")
304
+
305
+ # Event handlers
306
+ def process_screenshot(html_code, width, height, full_page, wait_time):
307
+ screenshot, error = render_screenshot(html_code, width, height, full_page, wait_time)
308
+ if error:
309
+ return None, error, gr.update(visible=True), gr.update(visible=False)
310
+ return screenshot, None, gr.update(visible=False), gr.update(visible=True)
311
+
312
+ screenshot_btn.click(
313
+ fn=process_screenshot,
314
+ inputs=[html_input, width_input, height_input, full_page, wait_time],
315
+ outputs=[screenshot_output, error_display, error_display, download_btn]
316
  )
317
+
318
+ clear_btn.click(
319
+ fn=lambda: (
320
+ "<!-- Enter your HTML here -->\n<h1>Hello, World!</h1>\n<p>This is a sample HTML page.</p>",
321
+ None,
322
+ gr.update(visible=False),
323
+ gr.update(visible=False)
324
+ ),
325
+ inputs=None,
326
+ outputs=[html_input, screenshot_output, error_display, download_btn]
 
 
 
 
 
 
327
  )
328
 
329
+ # Launch the app
330
  if __name__ == "__main__":
331
  demo.launch()