Skip to content

Commit 3ee5415

Browse files
committed
Add Code Image Generator project code
1 parent df1d722 commit 3ee5415

File tree

32 files changed

+1003
-0
lines changed

32 files changed

+1003
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import base64
2+
3+
from flask import Flask, render_template, session, request, redirect, url_for
4+
from pygments import highlight
5+
from pygments.formatters import HtmlFormatter
6+
from pygments.lexers import Python3Lexer
7+
from pygments.styles import get_all_styles
8+
9+
from utils import take_screenshot
10+
11+
app = Flask(__name__)
12+
app.secret_key = "mysecretkey"
13+
14+
PLACEHOLDER_CODE = "print('Hello, World!')"
15+
DEFAULT_STYLE = "monokai"
16+
NO_CODE_FALLBACK = "# No Code Entered"
17+
18+
19+
@app.route("/", methods=["GET"])
20+
def code():
21+
if session.get("code") is None:
22+
session["code"] = PLACEHOLDER_CODE
23+
lines = session["code"].split("\n")
24+
context = {
25+
"message": "Paste Your Python Code 🐍",
26+
"code": session["code"],
27+
"num_lines": len(lines),
28+
"max_chars": len(max(lines, key=len)),
29+
}
30+
return render_template("code_input.html", **context)
31+
32+
33+
@app.route("/save_code", methods=["POST"])
34+
def save_code():
35+
session["code"] = request.form.get("code") or NO_CODE_FALLBACK
36+
return redirect(url_for("code"))
37+
38+
39+
@app.route("/reset_session", methods=["GET"])
40+
def reset_session():
41+
session.clear()
42+
session["code"] = PLACEHOLDER_CODE
43+
return redirect(url_for("code"))
44+
45+
46+
@app.route("/save_style", methods=["POST"])
47+
def save_style():
48+
if request.form.get("style") is not None:
49+
session["style"] = request.form.get("style")
50+
if request.form.get("code") is not None:
51+
session["code"] = request.form.get("code") or NO_CODE_FALLBACK
52+
return redirect(url_for("style"))
53+
54+
55+
@app.route("/style", methods=["GET"])
56+
def style():
57+
if session.get("style") is None:
58+
session["style"] = DEFAULT_STYLE
59+
formatter = HtmlFormatter(style=session["style"])
60+
context = {
61+
"message": "Select Your Style 🎨",
62+
"code": session["code"],
63+
"all_styles": list(get_all_styles()),
64+
"style": session["style"],
65+
"style_bg_color": formatter.style.background_color,
66+
"style_definitions": formatter.get_style_defs(),
67+
"highlighted_code": highlight(
68+
session["code"], Python3Lexer(), formatter
69+
),
70+
}
71+
return render_template("style_selection.html", **context)
72+
73+
74+
@app.route("/image", methods=["GET"])
75+
def image():
76+
session_dict = {
77+
"name": app.config["SESSION_COOKIE_NAME"],
78+
"value": request.cookies.get(app.config["SESSION_COOKIE_NAME"]),
79+
"url": request.host_url,
80+
}
81+
target_url = request.host_url + url_for("style")
82+
image_bytes = take_screenshot(target_url, session_dict)
83+
context = {
84+
"message": "Done! 🎉",
85+
"image_b64": base64.b64encode(image_bytes).decode("utf-8"),
86+
}
87+
return render_template("image.html", **context)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Flask==2.3.2
2+
playwright==1.35.0
3+
Pygments==2.15.1
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
* {
2+
box-sizing: border-box;
3+
}
4+
5+
body {
6+
font-family: sans-serif;
7+
font-size: 20px;
8+
line-height: 1.3em;
9+
}
10+
11+
main {
12+
display: flex;
13+
justify-content: center;
14+
text-align: center;
15+
}
16+
17+
h1 {
18+
font-size: 2em;
19+
text-align: center;
20+
line-height: 1em;
21+
}
22+
23+
h1 a {
24+
text-decoration: none;
25+
color: inherit;
26+
}
27+
28+
span {
29+
color: deepskyblue;
30+
}
31+
32+
button,
33+
select {
34+
cursor: pointer;
35+
font-size: 100%;
36+
margin: 0.25em;
37+
min-width: 8em;
38+
}
39+
40+
.controls {
41+
margin: 1em 0;
42+
display: flex;
43+
justify-content: center;
44+
align-items: center;
45+
flex-wrap: wrap;
46+
47+
}
48+
49+
.code {
50+
font-family: monospace;
51+
box-shadow: #767676 0px 20px 30px -10px;
52+
border-radius: 0.5em;
53+
padding: 1em 2em;
54+
min-width: 32em;
55+
max-width: 100%;
56+
min-height: 12em;
57+
text-align: left;
58+
border: 1px solid #cecece;
59+
margin: 1em;
60+
line-height: 1.4em;
61+
}
62+
63+
.code_picture {
64+
text-align: center;
65+
}
66+
67+
img {
68+
margin: 1em 0;
69+
border: 1px solid #cecece;
70+
max-width: 80%;
71+
padding: 1em;
72+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8"/>
5+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
6+
<title>
7+
Code to Image {% block title %}{% endblock %}
8+
</title>
9+
<link rel="stylesheet"
10+
type="text/css"
11+
href="{{ url_for('static', filename='styles.css') }}"/>
12+
</head>
13+
<body>
14+
<h1>
15+
<a href="{{ url_for('code') }}">Code to Image</a>: <span>{{ message }}</span>
16+
</h1>
17+
<main>
18+
{% block content %}
19+
{% endblock content %}
20+
</main>
21+
</body>
22+
</html>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{% extends "base.html" %}
2+
{% block title %}
3+
- Code Input
4+
{% endblock title %}
5+
{% block content %}
6+
<style>
7+
.code {
8+
min-height: calc({{ num_lines }}em * 1.5 + 2 * 1em);
9+
min-width: calc({{ max_chars }}ch + 4 * 2em);
10+
}
11+
</style>
12+
<form>
13+
<textarea class="code"
14+
name="code"
15+
placeholder="Paste your Python code here">{{ code }}</textarea>
16+
<div class="controls">
17+
<button formmethod="get" formaction="{{ url_for("reset_session") }}">
18+
Reset Session ♻️
19+
</button>
20+
<button formmethod="post" formaction="{{ url_for("save_code") }}">
21+
Save Code 💾
22+
</button>
23+
<button formmethod="post" formaction="{{ url_for("save_style") }}">
24+
Next ➡️
25+
</button>
26+
</div>
27+
</form>
28+
{% endblock content %}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{% extends "base.html" %}
2+
{% block title %}
3+
- Your Image
4+
{% endblock title %}
5+
{% block content %}
6+
<form>
7+
{% if image_b64 %}
8+
<div class="code_picture">
9+
<img src="data:image/png;base64,{{ image_b64 | safe }}"
10+
alt="Code Image" />
11+
</div>
12+
<a href="data:image/png;base64,{{ image_b64 | safe }}"
13+
download="Your_CodeImage.png">
14+
Download Your Code Image ⤵️
15+
</a>
16+
{% endif %}
17+
<div class="controls">
18+
<button formmethod="get" formaction="{{ url_for("code") }}">
19+
🔄 Back to Start
20+
</button>
21+
</div>
22+
</form>
23+
{% endblock content %}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{% extends "base.html" %}
2+
{% block title %}
3+
- Style Selection
4+
{% endblock title %}
5+
{% block content %}
6+
<style>
7+
{{ style_definitions }}
8+
9+
.code {
10+
background-color: {{ style_bg_color }};
11+
}
12+
</style>
13+
<script>
14+
document.addEventListener("DOMContentLoaded", () => {
15+
document.querySelector("select").addEventListener("change", () => {
16+
document.querySelector("form").submit();
17+
});
18+
});
19+
</script>
20+
<form method="post" action="{{ url_for("save_style") }}">
21+
<div class="controls">
22+
<select name="style">
23+
{% for style_name in all_styles %}
24+
<option value="{{ style_name }}"
25+
{% if style_name == style %}selected{% endif %}>
26+
{{ style_name }}
27+
</option>
28+
{% endfor %}
29+
</select>
30+
</div>
31+
<div class="code">
32+
{{ highlighted_code | safe }}
33+
</div>
34+
<div class="controls">
35+
<button formmethod="get" formaction="{{ url_for("code") }}">
36+
⬅️ Back
37+
</button>
38+
<button formmethod="get" formaction="{{ url_for("image") }}">
39+
Create Image 📸
40+
</button>
41+
</div>
42+
</form>
43+
{% endblock content %}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from playwright.sync_api import sync_playwright
2+
3+
4+
def take_screenshot(target, session_dict):
5+
with sync_playwright() as playwright:
6+
webkit = playwright.webkit
7+
browser = webkit.launch()
8+
browser_context = browser.new_context(device_scale_factor=2)
9+
browser_context.add_cookies([session_dict])
10+
page = browser_context.new_page()
11+
page.goto(target)
12+
screenshot_bytes = page.locator(".code").screenshot()
13+
browser.close()
14+
return screenshot_bytes
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from flask import Flask, render_template
2+
3+
app = Flask(__name__)
4+
5+
6+
@app.route("/", methods=["GET"])
7+
def code():
8+
context = {
9+
"message": "Paste Your Python Code 🐍",
10+
}
11+
return render_template("code_input.html", **context)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Flask==2.3.2
2+
playwright==1.35.0
3+
Pygments==2.15.1

0 commit comments

Comments
 (0)