admin commited on
Commit
6c64dd2
·
1 Parent(s): cff22cb
Files changed (3) hide show
  1. src/modules/app.py +37 -10
  2. src/modules/link.py +50 -18
  3. 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
- with gr.Row():
32
- pos_x = gr.Number(91, label="Position X", minimum=0, maximum=1000, step=1)
33
- pos_y = gr.Number(55, label="Position Y", minimum=0, maximum=1000, step=1)
34
- pos_px = gr.Number(14, label="Position Padding", minimum=0, maximum=1000, step=1)
35
- pos_py = gr.Number(11, label="Position Padding", minimum=0, maximum=1000, step=1)
 
 
 
 
 
 
 
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
- pos_x,
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, pos_x, pos_y, pos_px, pos_py):
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
- for idx, png in enumerate(qr_pngs):
 
 
 
 
 
 
 
163
  try:
164
- png = ImageProcessor.combine_images(layer, [png], positions)
 
 
 
 
 
 
165
  if common_images:
166
- png = ImageProcessor.combine_images(png, common_images)
 
167
  if individual_images:
168
- for im in individual_images:
169
- png = ImageProcessor.combine_images(png, [im.name])
170
- with open(png, "rb") as image_file:
171
- encoded_string = base64.b64encode(image_file.read()).decode()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- card_output_paths = []
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
- card_output_paths.append(card_output_path)
 
 
 
 
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
- *card_output_paths,
 
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, qr_png_output_paths, zip_output_path, all_json_responses
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
- file_path = os.path.join(temp_dir, 'layer.png')
49
- img.save(file_path, format="PNG")
50
- return file_path
51
 
52
- @staticmethod
53
- def combine_images(base_image_path, overlay_image_paths, positions=None, temp_dir=None):
54
  try:
55
- if temp_dir is None:
56
- _, temp_dir = Utils.get_tempdir()
57
- with open(base_image_path, 'rb') as f:
58
- base_img = Image.open(f).convert("RGBA")
59
- for i, overlay_path in enumerate(overlay_image_paths):
60
- with open(overlay_path, 'rb') as f:
61
- overlay_img = Image.open(f).convert("RGBA")
62
- if positions and i < len(positions):
63
- position = positions[i]
64
- else:
65
- position = (0, 0)
66
- overlay_img_resized = ImageOps.contain(overlay_img, base_img.size)
67
- base_img.paste(overlay_img_resized, position, overlay_img_resized)
68
-
69
- file_path = os.path.join(temp_dir, 'combined.png')
70
- base_img.save(file_path, format="PNG")
71
- return file_path
 
 
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