Spaces:
Sleeping
Sleeping
admin
commited on
Commit
·
6c64dd2
1
Parent(s):
cff22cb
update
Browse files- src/modules/app.py +37 -10
- src/modules/link.py +50 -18
- src/modules/main.py +23 -23
src/modules/app.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
import gradio as gr
|
|
|
|
|
2 |
|
3 |
from link import UrlProcessor
|
4 |
# from main import TextProcessor
|
@@ -10,6 +12,26 @@ For more information, see
|
|
10 |
- https://app.bitly.com/settings/api
|
11 |
'''
|
12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
with gr.Blocks() as demo:
|
14 |
gr.Markdown(description)
|
15 |
with gr.Row():
|
@@ -28,22 +50,30 @@ with gr.Blocks() as demo:
|
|
28 |
cm_img_input = gr.Files(label='Insert common images')
|
29 |
idv_img_input = gr.Files(label='Insert individual images')
|
30 |
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
# positions = [(10, 10), (int(0.9 * s * 3.7795), int(0.9 * 55 * 3.7795))]
|
38 |
with gr.Column():
|
39 |
file_output = gr.File(label="Download ZIP")
|
40 |
with gr.Accordion(open=True, label="Gallery"):
|
41 |
-
gallery_output = gr.Gallery(label="PNG Gallery")
|
42 |
with gr.Accordion(open=False, label="Summary"):
|
43 |
json_output = gr.JSON(label="JSON Summary")
|
44 |
with gr.Accordion(open=False, label="Preview"):
|
45 |
preview_output = gr.TextArea(label="Raw Text")
|
46 |
|
|
|
47 |
# example_temp_html = TextProcessor.apply_style('demo/demo.md', 'style.css') # TODO
|
48 |
with open('demo/demo.md') as f:
|
49 |
demo_txt = f.read()
|
@@ -68,10 +98,7 @@ with gr.Blocks() as demo:
|
|
68 |
qr_checkbox,
|
69 |
cm_img_input,
|
70 |
idv_img_input,
|
71 |
-
|
72 |
-
pos_y,
|
73 |
-
pos_px,
|
74 |
-
pos_py,
|
75 |
],
|
76 |
outputs=[preview_output, gallery_output, file_output, json_output]
|
77 |
)
|
|
|
1 |
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
import itertools
|
4 |
|
5 |
from link import UrlProcessor
|
6 |
# from main import TextProcessor
|
|
|
12 |
- https://app.bitly.com/settings/api
|
13 |
'''
|
14 |
|
15 |
+
def calculate_positions(image_count, card_size=(91, 55), page_size=(210, 297), padding=(14, 11)):
|
16 |
+
positions = []
|
17 |
+
x, y = 0, 0
|
18 |
+
pos_px, pos_py = padding
|
19 |
+
|
20 |
+
for i in range(image_count):
|
21 |
+
if x + card_size[0] > page_size[0]: # Check for horizontal overflow
|
22 |
+
x = 0
|
23 |
+
y += card_size[1] + pos_py
|
24 |
+
|
25 |
+
if y + card_size[1] > page_size[1]: # Check for vertical overflow
|
26 |
+
x = 0
|
27 |
+
y = 0
|
28 |
+
|
29 |
+
positions.append((x, y, pos_px, pos_py))
|
30 |
+
x += card_size[0] + pos_px
|
31 |
+
print(positions)
|
32 |
+
return positions
|
33 |
+
|
34 |
+
|
35 |
with gr.Blocks() as demo:
|
36 |
gr.Markdown(description)
|
37 |
with gr.Row():
|
|
|
50 |
cm_img_input = gr.Files(label='Insert common images')
|
51 |
idv_img_input = gr.Files(label='Insert individual images')
|
52 |
|
53 |
+
headers = ['ImageId', 'PosX', 'PosY', 'PosPx', 'PosPy']
|
54 |
+
@gr.render(inputs=[cm_img_input, idv_img_input])
|
55 |
+
def update_df(*files):
|
56 |
+
image_count = sum(len(file) for file in files if file is not None)
|
57 |
+
positions = calculate_positions(image_count)
|
58 |
+
df = pd.DataFrame(positions, columns=['PosX', 'PosY', 'PosPx', 'PosPy'])
|
59 |
+
df.insert(0, 'ImageId', range(1, image_count + 1))
|
60 |
+
return df
|
61 |
+
|
62 |
+
pos_df = gr.Dataframe(headers=headers, label="Positions")
|
63 |
+
|
64 |
+
cm_img_input.change(update_df, cm_img_input, pos_df)
|
65 |
|
66 |
# positions = [(10, 10), (int(0.9 * s * 3.7795), int(0.9 * 55 * 3.7795))]
|
67 |
with gr.Column():
|
68 |
file_output = gr.File(label="Download ZIP")
|
69 |
with gr.Accordion(open=True, label="Gallery"):
|
70 |
+
gallery_output = gr.Gallery(object_fit='contain', label="PNG Gallery")
|
71 |
with gr.Accordion(open=False, label="Summary"):
|
72 |
json_output = gr.JSON(label="JSON Summary")
|
73 |
with gr.Accordion(open=False, label="Preview"):
|
74 |
preview_output = gr.TextArea(label="Raw Text")
|
75 |
|
76 |
+
|
77 |
# example_temp_html = TextProcessor.apply_style('demo/demo.md', 'style.css') # TODO
|
78 |
with open('demo/demo.md') as f:
|
79 |
demo_txt = f.read()
|
|
|
98 |
qr_checkbox,
|
99 |
cm_img_input,
|
100 |
idv_img_input,
|
101 |
+
pos_df,
|
|
|
|
|
|
|
102 |
],
|
103 |
outputs=[preview_output, gallery_output, file_output, json_output]
|
104 |
)
|
src/modules/link.py
CHANGED
@@ -5,10 +5,12 @@ import base64
|
|
5 |
import json
|
6 |
import qrcode
|
7 |
import markdown
|
|
|
8 |
from io import BytesIO
|
|
|
9 |
|
10 |
from utils import Utils
|
11 |
-
from main import ImageProcessor
|
12 |
|
13 |
|
14 |
BITLY_ACCESS_TOKEN = os.getenv('BITLY_ACCESS_TOKEN', None)
|
@@ -81,7 +83,7 @@ class UrlProcessor:
|
|
81 |
|
82 |
|
83 |
@staticmethod
|
84 |
-
def shorten_and_generate_qr(access_token, api_url, txt, shorten, generate_qr, common_images, individual_images,
|
85 |
bitly = UrlProcessor(access_token, api_url)
|
86 |
|
87 |
urls = re.findall(r'http[s]?://\S+', txt)
|
@@ -94,11 +96,10 @@ class UrlProcessor:
|
|
94 |
qr_svgs = []
|
95 |
qr_pngs = []
|
96 |
card_htmls = []
|
|
|
97 |
index_path = 'index.html'
|
98 |
css_path = 'style.css'
|
99 |
|
100 |
-
positions = [(pos_px, pos_py), (int(0.9 * pos_x * 3.7795), int(0.9 * pos_y * 3.7795))]
|
101 |
-
|
102 |
for idx, url in enumerate(urls):
|
103 |
shorten_url, shorten_response_json = None, None
|
104 |
try:
|
@@ -157,21 +158,47 @@ class UrlProcessor:
|
|
157 |
except Exception as e:
|
158 |
results.append((str(e), url))
|
159 |
|
160 |
-
layer = ImageProcessor.create_layer()
|
161 |
|
162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
try:
|
164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
if common_images:
|
166 |
-
|
|
|
167 |
if individual_images:
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
-
card_html = f'<img src="data:image/png;base64,{encoded_string}" alt="Card"> '
|
174 |
-
card_htmls.append(card_html)
|
175 |
except Exception as e:
|
176 |
raise Exception(f"Error combining images: {e}")
|
177 |
|
@@ -197,13 +224,17 @@ class UrlProcessor:
|
|
197 |
for idx, png in enumerate(qr_pngs):
|
198 |
qr_png_output_paths.append(png)
|
199 |
|
200 |
-
|
201 |
os.makedirs(f'{tmpd}/chunks/cards', exist_ok=True)
|
202 |
for idx, card_html in enumerate(card_htmls):
|
203 |
card_output_path = os.path.join(tmpd, f'chunks/cards/CD{idx:05d}.html')
|
204 |
with open(card_output_path, 'w') as card_file:
|
205 |
card_file.write(card_html)
|
206 |
-
|
|
|
|
|
|
|
|
|
207 |
|
208 |
index_html = f'<html><head><link rel="stylesheet" type="text/css" href="{css_path}"></head><body>'
|
209 |
for card_html in card_htmls:
|
@@ -220,7 +251,8 @@ class UrlProcessor:
|
|
220 |
markdown_output_path,
|
221 |
*qr_svg_output_paths,
|
222 |
*qr_png_output_paths,
|
223 |
-
*
|
|
|
224 |
index_output_path,
|
225 |
css_path
|
226 |
]
|
@@ -228,5 +260,5 @@ class UrlProcessor:
|
|
228 |
except Exception as e:
|
229 |
return f"An error occurred: {str(e)}", None, None, []
|
230 |
|
231 |
-
return markdown_links,
|
232 |
|
|
|
5 |
import json
|
6 |
import qrcode
|
7 |
import markdown
|
8 |
+
import itertools
|
9 |
from io import BytesIO
|
10 |
+
from PIL import ImageOps
|
11 |
|
12 |
from utils import Utils
|
13 |
+
from main import Image, ImageProcessor
|
14 |
|
15 |
|
16 |
BITLY_ACCESS_TOKEN = os.getenv('BITLY_ACCESS_TOKEN', None)
|
|
|
83 |
|
84 |
|
85 |
@staticmethod
|
86 |
+
def shorten_and_generate_qr(access_token, api_url, txt, shorten, generate_qr, common_images, individual_images, positions_df):
|
87 |
bitly = UrlProcessor(access_token, api_url)
|
88 |
|
89 |
urls = re.findall(r'http[s]?://\S+', txt)
|
|
|
96 |
qr_svgs = []
|
97 |
qr_pngs = []
|
98 |
card_htmls = []
|
99 |
+
card_pngs = []
|
100 |
index_path = 'index.html'
|
101 |
css_path = 'style.css'
|
102 |
|
|
|
|
|
103 |
for idx, url in enumerate(urls):
|
104 |
shorten_url, shorten_response_json = None, None
|
105 |
try:
|
|
|
158 |
except Exception as e:
|
159 |
results.append((str(e), url))
|
160 |
|
|
|
161 |
|
162 |
+
processor = ImageProcessor()
|
163 |
+
layer = processor.create_layer()
|
164 |
+
# for qr in qr_svgs:
|
165 |
+
# with open(qr, "rb") as image_file:
|
166 |
+
# encoded_string = base64.b64encode(image_file.read()).decode()
|
167 |
+
|
168 |
+
os.makedirs(f'{tmpd}/img/cards', exist_ok=True)
|
169 |
+
for idx, (qr, indv) in enumerate(itertools.product(qr_pngs, individual_images), start=1):
|
170 |
try:
|
171 |
+
i = int(idx) - 1
|
172 |
+
print(i)
|
173 |
+
pos_x = int(positions_df.loc[i, 'PosX'])
|
174 |
+
pos_y = int(positions_df.loc[i, 'PosY'])
|
175 |
+
|
176 |
+
size = (int(91 * 3.7795), int(55 * 3.7795))
|
177 |
+
|
178 |
if common_images:
|
179 |
+
for common_img in common_images:
|
180 |
+
layer = processor.combine_images(layer, common_img, size, (pos_x, pos_y))
|
181 |
if individual_images:
|
182 |
+
layer = processor.combine_images(layer, indv, size, (pos_x, pos_y))
|
183 |
+
|
184 |
+
if qr:
|
185 |
+
qr_size = (int(size[0] * 0.1), int(size[1] * 0.1))
|
186 |
+
qr_pos = (int(0.9 * pos_x * 3.7795), int(0.9 * pos_y * 3.7795))
|
187 |
+
layer = processor.combine_images(layer, qr, qr_size, qr_pos)
|
188 |
+
|
189 |
+
|
190 |
+
card_png_path = f'img/cards/CD{idx:05d}.png'
|
191 |
+
card_path = os.path.join(tmpd, card_png_path)
|
192 |
+
|
193 |
+
layer.save(card_path, format="PNG")
|
194 |
+
card_pngs.append(card_path)
|
195 |
+
|
196 |
+
with open(card_path, "rb") as image_file:
|
197 |
+
encoded_string = base64.b64encode(image_file.read()).decode()
|
198 |
+
|
199 |
+
card_html = f'<img src="data:image/png;base64,{encoded_string}" alt="Card"> '
|
200 |
+
card_htmls.append(card_html)
|
201 |
|
|
|
|
|
202 |
except Exception as e:
|
203 |
raise Exception(f"Error combining images: {e}")
|
204 |
|
|
|
224 |
for idx, png in enumerate(qr_pngs):
|
225 |
qr_png_output_paths.append(png)
|
226 |
|
227 |
+
card_svg_output_paths = []
|
228 |
os.makedirs(f'{tmpd}/chunks/cards', exist_ok=True)
|
229 |
for idx, card_html in enumerate(card_htmls):
|
230 |
card_output_path = os.path.join(tmpd, f'chunks/cards/CD{idx:05d}.html')
|
231 |
with open(card_output_path, 'w') as card_file:
|
232 |
card_file.write(card_html)
|
233 |
+
card_svg_output_paths.append(card_output_path)
|
234 |
+
|
235 |
+
card_png_output_paths = []
|
236 |
+
for idx, png in enumerate(card_pngs):
|
237 |
+
card_png_output_paths.append(png)
|
238 |
|
239 |
index_html = f'<html><head><link rel="stylesheet" type="text/css" href="{css_path}"></head><body>'
|
240 |
for card_html in card_htmls:
|
|
|
251 |
markdown_output_path,
|
252 |
*qr_svg_output_paths,
|
253 |
*qr_png_output_paths,
|
254 |
+
*card_svg_output_paths,
|
255 |
+
*card_png_output_paths,
|
256 |
index_output_path,
|
257 |
css_path
|
258 |
]
|
|
|
260 |
except Exception as e:
|
261 |
return f"An error occurred: {str(e)}", None, None, []
|
262 |
|
263 |
+
return markdown_links, card_png_output_paths, zip_output_path, all_json_responses
|
264 |
|
src/modules/main.py
CHANGED
@@ -33,8 +33,9 @@ class TextProcessor:
|
|
33 |
return temp_html_path
|
34 |
|
35 |
class ImageProcessor:
|
|
|
|
|
36 |
|
37 |
-
@staticmethod
|
38 |
def create_layer(layout='horizontal', size=(91 * 3.7795, 55 * 3.7795)):
|
39 |
ts, temp_dir = Utils.get_tempdir()
|
40 |
size = (int(size[0]), int(size[1])) # Convert to integers
|
@@ -45,30 +46,29 @@ class ImageProcessor:
|
|
45 |
elif layout == 'horizontal':
|
46 |
draw.rectangle([(0, 0), (size[0], size[1])], outline="lightgray")
|
47 |
|
48 |
-
|
49 |
-
img.save(file_path, format="PNG")
|
50 |
-
return file_path
|
51 |
|
52 |
-
|
53 |
-
def combine_images(base_image_path, overlay_image_paths, positions=None, temp_dir=None):
|
54 |
try:
|
55 |
-
|
56 |
-
|
57 |
-
with open(
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
|
|
|
|
72 |
except Exception as e:
|
73 |
raise Exception(f"Error combining images: {e}")
|
74 |
# @staticmethod
|
|
|
33 |
return temp_html_path
|
34 |
|
35 |
class ImageProcessor:
|
36 |
+
def __init__(self):
|
37 |
+
_, self.temp_dir = Utils.get_tempdir()
|
38 |
|
|
|
39 |
def create_layer(layout='horizontal', size=(91 * 3.7795, 55 * 3.7795)):
|
40 |
ts, temp_dir = Utils.get_tempdir()
|
41 |
size = (int(size[0]), int(size[1])) # Convert to integers
|
|
|
46 |
elif layout == 'horizontal':
|
47 |
draw.rectangle([(0, 0), (size[0], size[1])], outline="lightgray")
|
48 |
|
49 |
+
return img
|
|
|
|
|
50 |
|
51 |
+
def combine_images(self, base_image, overlay_path, size, position):
|
|
|
52 |
try:
|
53 |
+
base_img = base_image.convert("RGBA")
|
54 |
+
|
55 |
+
with open(overlay_path, 'rb') as f:
|
56 |
+
overlay_img = Image.open(f).convert("RGBA")
|
57 |
+
|
58 |
+
overlay_img_resized = overlay_img.resize(size)
|
59 |
+
|
60 |
+
if position is None:
|
61 |
+
position = (0, 0)
|
62 |
+
|
63 |
+
# Create a blank image with the same size as base_img
|
64 |
+
temp_img = Image.new('RGBA', base_img.size, (255, 255, 255, 0))
|
65 |
+
|
66 |
+
temp_img.paste(overlay_img_resized, position, overlay_img_resized)
|
67 |
+
|
68 |
+
# Alpha composite the temporary image with the base image
|
69 |
+
base_img = Image.alpha_composite(base_img, temp_img)
|
70 |
+
|
71 |
+
return base_img
|
72 |
except Exception as e:
|
73 |
raise Exception(f"Error combining images: {e}")
|
74 |
# @staticmethod
|