dankcli-lib 0.5.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dankcli_lib/__init__.py +0 -0
- dankcli_lib/__main__.py +117 -0
- dankcli_lib/caption.py +251 -0
- dankcli_lib/fonts/HelveticaNeue.ttf +0 -0
- dankcli_lib/fonts/arial.ttf +0 -0
- dankcli_lib/functions.py +54 -0
- dankcli_lib-0.5.9.dist-info/METADATA +92 -0
- dankcli_lib-0.5.9.dist-info/RECORD +11 -0
- dankcli_lib-0.5.9.dist-info/WHEEL +5 -0
- dankcli_lib-0.5.9.dist-info/licenses/LICENSE +21 -0
- dankcli_lib-0.5.9.dist-info/top_level.txt +1 -0
dankcli_lib/__init__.py
ADDED
|
File without changes
|
dankcli_lib/__main__.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import argparse
|
|
4
|
+
import datetime
|
|
5
|
+
from .caption import Caption
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_file_name():
|
|
9
|
+
"""Generate timestamp-based filename."""
|
|
10
|
+
return datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parse_color(color_str):
|
|
14
|
+
"""Parse color string in format R,G,B or R G B."""
|
|
15
|
+
if not color_str:
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
# Try comma-separated first
|
|
19
|
+
if ',' in color_str:
|
|
20
|
+
try:
|
|
21
|
+
r, g, b = map(int, color_str.split(','))
|
|
22
|
+
return (r, g, b)
|
|
23
|
+
except ValueError:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
# Try space-separated
|
|
27
|
+
try:
|
|
28
|
+
r, g, b = map(int, color_str.split())
|
|
29
|
+
return (r, g, b)
|
|
30
|
+
except ValueError:
|
|
31
|
+
raise ValueError(f"Invalid color format. Use R,G,B or R G B (e.g., '255,255,255')")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main():
|
|
35
|
+
parser = argparse.ArgumentParser(prog="dankcli_lib", description="Add captions to images with various styling options")
|
|
36
|
+
|
|
37
|
+
# Required arguments
|
|
38
|
+
parser.add_argument("img", help="Path to the source image")
|
|
39
|
+
parser.add_argument("text", help="Text to caption (use \\n for new lines)")
|
|
40
|
+
|
|
41
|
+
# Output options
|
|
42
|
+
parser.add_argument("-f", "--filename", help="Custom output filename (without extension)")
|
|
43
|
+
|
|
44
|
+
# Font options
|
|
45
|
+
parser.add_argument("--font", default="arial.ttf", help="Path to font file (default: arial.ttf)")
|
|
46
|
+
|
|
47
|
+
# Top text options
|
|
48
|
+
parser.add_argument("--top_font_color", help="Top text color as R,G,B or R G B (e.g., '0,0,0' for black)")
|
|
49
|
+
parser.add_argument("--top_bg_color", help="Top background color as R,G,B or R G B (e.g., '255,255,255' for white)")
|
|
50
|
+
|
|
51
|
+
# Bottom text options
|
|
52
|
+
parser.add_argument("--bottom_text", help="Text to place at the bottom of the image")
|
|
53
|
+
parser.add_argument("--bottom_text_box", action="store_true", default=True,
|
|
54
|
+
help="Add white box for bottom text (default: True)")
|
|
55
|
+
parser.add_argument("--no-bottom-text-box", dest="bottom_text_box", action="store_false",
|
|
56
|
+
help="Overlay bottom text directly on image without box")
|
|
57
|
+
parser.add_argument("--bottom_font_color", help="Bottom text color as R,G,B or R G B (e.g., '255,255,255' for white)")
|
|
58
|
+
parser.add_argument("--bottom_bg_color", help="Bottom background color as R,G,B or R G B (e.g., '255,255,255' for white)")
|
|
59
|
+
|
|
60
|
+
# Separator line options
|
|
61
|
+
parser.add_argument("--separator_line", action="store_true",
|
|
62
|
+
help="Draw a line separating the caption from the image")
|
|
63
|
+
parser.add_argument("--separator_color", default="0,0,0",
|
|
64
|
+
help="Separator line color as R,G,B or R G B (default: '0,0,0' for black)")
|
|
65
|
+
|
|
66
|
+
args = parser.parse_args()
|
|
67
|
+
|
|
68
|
+
# Resolve image path
|
|
69
|
+
img_path = os.path.abspath(os.path.expanduser(args.img))
|
|
70
|
+
|
|
71
|
+
# Check if image exists
|
|
72
|
+
if not os.path.exists(img_path):
|
|
73
|
+
print(f"Error: Image file not found: {img_path}")
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
# Parse color arguments
|
|
78
|
+
top_font_color = parse_color(args.top_font_color) if args.top_font_color else None
|
|
79
|
+
top_bg_color = parse_color(args.top_bg_color) if args.top_bg_color else None
|
|
80
|
+
bottom_font_color = parse_color(args.bottom_font_color) if args.bottom_font_color else None
|
|
81
|
+
bottom_bg_color = parse_color(args.bottom_bg_color) if args.bottom_bg_color else None
|
|
82
|
+
separator_color = parse_color(args.separator_color)
|
|
83
|
+
|
|
84
|
+
# Create Caption instance with all options
|
|
85
|
+
caption = Caption(
|
|
86
|
+
img_path,
|
|
87
|
+
args.text,
|
|
88
|
+
font_path=args.font,
|
|
89
|
+
separator_line=args.separator_line,
|
|
90
|
+
separator_line_color=separator_color,
|
|
91
|
+
bottom_text=args.bottom_text,
|
|
92
|
+
bottom_text_box=args.bottom_text_box,
|
|
93
|
+
top_font_color=top_font_color,
|
|
94
|
+
bottom_font_color=bottom_font_color,
|
|
95
|
+
top_background_color=top_bg_color,
|
|
96
|
+
bottom_background_color=bottom_bg_color
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Determine output filename and extension
|
|
100
|
+
out_name = args.filename if args.filename else get_file_name()
|
|
101
|
+
# Check if image has transparency or if we're using overlay text (no box)
|
|
102
|
+
# PNG supports transparency better, but we'll let the user specify format eventually
|
|
103
|
+
is_jpeg = img_path.lower().endswith(('.jpg', '.jpeg'))
|
|
104
|
+
extension = "jpg" if is_jpeg else "png"
|
|
105
|
+
output_path = f"{out_name}.{extension}"
|
|
106
|
+
|
|
107
|
+
# Generate and save
|
|
108
|
+
caption.save(output_path)
|
|
109
|
+
print(f"Meme saved successfully: {output_path}")
|
|
110
|
+
|
|
111
|
+
except Exception as e:
|
|
112
|
+
print(f"Error: Could not process image. {e}")
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
main()
|
dankcli_lib/caption.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
from PIL import Image, ImageFont, ImageDraw
|
|
2
|
+
import math, io
|
|
3
|
+
|
|
4
|
+
class Caption:
|
|
5
|
+
"""Handles image captioning with text overlay."""
|
|
6
|
+
|
|
7
|
+
# Default styling constants
|
|
8
|
+
TOP_PADDING = 10
|
|
9
|
+
BOTTOM_PADDING = 10
|
|
10
|
+
WIDTH_PADDING = 10
|
|
11
|
+
MINIMUM_FONT_SIZE = 13
|
|
12
|
+
HW_ASPECT_RATIO_THRESHOLD = 1.666
|
|
13
|
+
MAX_BOTTOM_TEXT_HEIGHT_RATIO = 0.334
|
|
14
|
+
|
|
15
|
+
def __init__(self, image_path, text, font_path="arial.ttf", separator_line=False, separator_line_color=None,
|
|
16
|
+
bottom_text=None, bottom_text_box=True,
|
|
17
|
+
top_font_color=None, bottom_font_color=None, top_background_color=None, bottom_background_color=None):
|
|
18
|
+
"""
|
|
19
|
+
Initialize Caption with an image and text.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
image_path: Path to the source image
|
|
23
|
+
text: Caption text (supports \\n for newlines)
|
|
24
|
+
font_path: Path to font file (defaults to arial.ttf)
|
|
25
|
+
separator_line: Whether to draw a black line between text and image
|
|
26
|
+
bottom_text: Optional text to place at the bottom of the image
|
|
27
|
+
bottom_text_box: If True, adds white box for bottom text. If False, overlays text on image
|
|
28
|
+
top_font_color: Font color for top text (defaults to black (0,0,0) if None)
|
|
29
|
+
bottom_font_color: Font color for bottom text (defaults to white (255,255,255) if None)
|
|
30
|
+
"""
|
|
31
|
+
self.image = Image.open(image_path)
|
|
32
|
+
self.text = text.replace("\\n", "\n")
|
|
33
|
+
self.bottom_text = bottom_text.replace("\\n", "\n") if bottom_text else None
|
|
34
|
+
self.bottom_text_box = bottom_text_box
|
|
35
|
+
self.font_path = font_path
|
|
36
|
+
self.separator_line = separator_line
|
|
37
|
+
self.separator_line_color = separator_line_color if separator_line_color is not None else (0, 0, 0)
|
|
38
|
+
|
|
39
|
+
self.width, self.height = self.image.size
|
|
40
|
+
|
|
41
|
+
# Set default colors if not provided
|
|
42
|
+
self.top_font_color = top_font_color if top_font_color is not None else (0, 0, 0)
|
|
43
|
+
self.bottom_font_color = bottom_font_color if bottom_font_color is not None else (0, 0, 0)
|
|
44
|
+
|
|
45
|
+
self.top_background_color = top_background_color if top_background_color is not None else (255, 255, 255)
|
|
46
|
+
self.bottom_background_color = bottom_background_color if bottom_background_color is not None else (255, 255, 255)
|
|
47
|
+
|
|
48
|
+
# Initialize font
|
|
49
|
+
try:
|
|
50
|
+
font_size = self._calculate_font_size()
|
|
51
|
+
self.font = ImageFont.truetype(font_path, size=font_size)
|
|
52
|
+
except OSError:
|
|
53
|
+
self.font = ImageFont.load_default()
|
|
54
|
+
|
|
55
|
+
def _calculate_font_size(self):
|
|
56
|
+
"""Calculate appropriate font size based on image dimensions."""
|
|
57
|
+
temp_size = max(math.floor(self.height / 13), self.MINIMUM_FONT_SIZE)
|
|
58
|
+
|
|
59
|
+
# Scale down for very tall images
|
|
60
|
+
if self.height / self.width >= self.HW_ASPECT_RATIO_THRESHOLD:
|
|
61
|
+
return math.floor(temp_size / 1.5)
|
|
62
|
+
return temp_size
|
|
63
|
+
|
|
64
|
+
def _get_text_dimensions(self, text):
|
|
65
|
+
"""Get width and height of text with current font."""
|
|
66
|
+
bbox = self.font.getbbox(text)
|
|
67
|
+
return bbox[2] - bbox[0], bbox[3] - bbox[1]
|
|
68
|
+
|
|
69
|
+
def _wrap_text(self, text, max_width):
|
|
70
|
+
"""Wrap text to fit within max_width."""
|
|
71
|
+
lines = []
|
|
72
|
+
words = text.split(' ')
|
|
73
|
+
i = 0
|
|
74
|
+
|
|
75
|
+
while i < len(words):
|
|
76
|
+
line = ''
|
|
77
|
+
while (i < len(words) and
|
|
78
|
+
self._get_text_dimensions(line + words[i])[0] < max_width - self.WIDTH_PADDING):
|
|
79
|
+
line = line + words[i] + " "
|
|
80
|
+
i += 1
|
|
81
|
+
|
|
82
|
+
if not line:
|
|
83
|
+
line = words[i]
|
|
84
|
+
i += 1
|
|
85
|
+
|
|
86
|
+
lines.append(line.strip())
|
|
87
|
+
|
|
88
|
+
return '\n'.join(lines)
|
|
89
|
+
|
|
90
|
+
def _process_text(self):
|
|
91
|
+
"""Process and wrap text for rendering."""
|
|
92
|
+
wrapped_lines = "\n".join([
|
|
93
|
+
self._wrap_text(line, self.width)
|
|
94
|
+
for line in self.text.split("\n")
|
|
95
|
+
])
|
|
96
|
+
return wrapped_lines
|
|
97
|
+
|
|
98
|
+
def _calculate_text_height(self, text):
|
|
99
|
+
"""Calculate total height needed for text."""
|
|
100
|
+
line_list = text.split('\n')
|
|
101
|
+
if not line_list:
|
|
102
|
+
return 0
|
|
103
|
+
|
|
104
|
+
_, line_height = self._get_text_dimensions(line_list[0])
|
|
105
|
+
total_text_height = len(line_list) * (line_height * 1.2)
|
|
106
|
+
return int(total_text_height + self.TOP_PADDING + self.BOTTOM_PADDING)
|
|
107
|
+
|
|
108
|
+
def _get_text_position(self, text):
|
|
109
|
+
"""Calculate top-left corner position for centered text."""
|
|
110
|
+
line_list = text.split('\n')
|
|
111
|
+
max_w = max(self._get_text_dimensions(line)[0] for line in line_list)
|
|
112
|
+
return ((self.width - max_w) / 2, self.TOP_PADDING)
|
|
113
|
+
|
|
114
|
+
def _get_text_position_bottom(self, text, y_offset):
|
|
115
|
+
"""Calculate top-left corner position for centered bottom text."""
|
|
116
|
+
line_list = text.split('\n')
|
|
117
|
+
max_w = max(self._get_text_dimensions(line)[0] for line in line_list)
|
|
118
|
+
return ((self.width - max_w) / 2, y_offset + self.TOP_PADDING)
|
|
119
|
+
|
|
120
|
+
def _get_text_position_bottom_overlay(self, text):
|
|
121
|
+
"""Calculate position for bottom text overlay on the image."""
|
|
122
|
+
line_list = text.split('\n')
|
|
123
|
+
max_w = max(self._get_text_dimensions(line)[0] for line in line_list)
|
|
124
|
+
|
|
125
|
+
_, line_height = self._get_text_dimensions(line_list[0])
|
|
126
|
+
total_text_height = len(line_list) * (line_height * 1.2)
|
|
127
|
+
|
|
128
|
+
y_position = self.height - total_text_height - self.BOTTOM_PADDING
|
|
129
|
+
|
|
130
|
+
if y_position < self.TOP_PADDING:
|
|
131
|
+
y_position = self.TOP_PADDING
|
|
132
|
+
|
|
133
|
+
return ((self.width - max_w) / 2, y_position)
|
|
134
|
+
|
|
135
|
+
def generate(self):
|
|
136
|
+
"""
|
|
137
|
+
Generate the captioned image.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
PIL.Image: The captioned image
|
|
141
|
+
"""
|
|
142
|
+
wrapped_top_text = self._process_text()
|
|
143
|
+
top_text_height = self._calculate_text_height(wrapped_top_text)
|
|
144
|
+
|
|
145
|
+
bottom_text_height = 0
|
|
146
|
+
wrapped_bottom_text = None
|
|
147
|
+
if self.bottom_text:
|
|
148
|
+
wrapped_bottom_text = "\n".join([
|
|
149
|
+
self._wrap_text(line, self.width)
|
|
150
|
+
for line in self.bottom_text.split("\n")
|
|
151
|
+
])
|
|
152
|
+
bottom_text_height = self._calculate_text_height(wrapped_bottom_text)
|
|
153
|
+
|
|
154
|
+
if self.bottom_text_box:
|
|
155
|
+
max_allowed_height = int(self.height * self.MAX_BOTTOM_TEXT_HEIGHT_RATIO)
|
|
156
|
+
if bottom_text_height > max_allowed_height:
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
if self.bottom_text_box or not self.bottom_text:
|
|
160
|
+
total_height = top_text_height + self.height + bottom_text_height
|
|
161
|
+
canvas = Image.new("RGB", (self.width, total_height), (255, 255, 255))
|
|
162
|
+
canvas.paste(self.image, (0, top_text_height))
|
|
163
|
+
else:
|
|
164
|
+
total_height = top_text_height + self.height
|
|
165
|
+
canvas = Image.new("RGB", (self.width, total_height), (255, 255, 255))
|
|
166
|
+
canvas.paste(self.image, (0, top_text_height))
|
|
167
|
+
|
|
168
|
+
draw = ImageDraw.Draw(canvas)
|
|
169
|
+
|
|
170
|
+
if self.top_background_color:
|
|
171
|
+
draw.rectangle(
|
|
172
|
+
[(0, 0), (self.width, top_text_height)],
|
|
173
|
+
fill=self.top_background_color
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if self.separator_line:
|
|
177
|
+
line_y = top_text_height - 1
|
|
178
|
+
draw.line([(0, line_y), (self.width, line_y)], fill=self.separator_line_color, width=2)
|
|
179
|
+
|
|
180
|
+
top_text_pos = self._get_text_position(wrapped_top_text)
|
|
181
|
+
|
|
182
|
+
draw.multiline_text(
|
|
183
|
+
top_text_pos,
|
|
184
|
+
wrapped_top_text,
|
|
185
|
+
fill=self.top_font_color,
|
|
186
|
+
font=self.font,
|
|
187
|
+
align="center",
|
|
188
|
+
spacing=4
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if self.bottom_text and wrapped_bottom_text:
|
|
192
|
+
if self.bottom_text_box:
|
|
193
|
+
# Draw bottom background if specified
|
|
194
|
+
if self.bottom_background_color:
|
|
195
|
+
bottom_bg_y = top_text_height + self.height
|
|
196
|
+
draw.rectangle(
|
|
197
|
+
[(0, bottom_bg_y), (self.width, bottom_bg_y + bottom_text_height)],
|
|
198
|
+
fill=self.bottom_background_color
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
bottom_text_pos = self._get_text_position_bottom(wrapped_bottom_text, top_text_height + self.height)
|
|
202
|
+
|
|
203
|
+
draw.multiline_text(
|
|
204
|
+
bottom_text_pos,
|
|
205
|
+
wrapped_bottom_text,
|
|
206
|
+
fill=self.bottom_font_color,
|
|
207
|
+
font=self.font,
|
|
208
|
+
align="center",
|
|
209
|
+
spacing=4
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if self.separator_line:
|
|
213
|
+
line_y = top_text_height + self.height
|
|
214
|
+
draw.line([(0, line_y), (self.width, line_y)], fill=self.separator_line_color, width=2)
|
|
215
|
+
else:
|
|
216
|
+
overlay_text_pos = self._get_text_position_bottom_overlay(wrapped_bottom_text)
|
|
217
|
+
adjusted_y = overlay_text_pos[1] + top_text_height
|
|
218
|
+
|
|
219
|
+
draw.multiline_text(
|
|
220
|
+
(overlay_text_pos[0], adjusted_y),
|
|
221
|
+
wrapped_bottom_text,
|
|
222
|
+
fill=self.bottom_font_color,
|
|
223
|
+
font=self.font,
|
|
224
|
+
align="center",
|
|
225
|
+
spacing=4
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
return canvas
|
|
229
|
+
|
|
230
|
+
def to_buffer(self, format="JPEG"):
|
|
231
|
+
captioned_image = self.generate()
|
|
232
|
+
buffer = io.BytesIO()
|
|
233
|
+
captioned_image.save(buffer, format=format)
|
|
234
|
+
buffer.seek(0)
|
|
235
|
+
return buffer
|
|
236
|
+
|
|
237
|
+
def close(self):
|
|
238
|
+
"""Close the underlying PIL Image to free memory."""
|
|
239
|
+
if hasattr(self, 'image'):
|
|
240
|
+
self.image.close()
|
|
241
|
+
|
|
242
|
+
def save(self, output_path):
|
|
243
|
+
"""
|
|
244
|
+
Generate and save the captioned image.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
output_path: Path to save the output image
|
|
248
|
+
"""
|
|
249
|
+
captioned_image = self.generate()
|
|
250
|
+
captioned_image.save(output_path)
|
|
251
|
+
return output_path
|
|
Binary file
|
|
Binary file
|
dankcli_lib/functions.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from PIL import Image, ImageFont, ImageDraw
|
|
2
|
+
import math
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
TOP_PADDING = 10
|
|
6
|
+
BOTTOM_PADDING = 10
|
|
7
|
+
MINIMUM_FONT_SIZE = 13
|
|
8
|
+
HW_ASPECT_RATIO_THRESHOLD = 1.666
|
|
9
|
+
WIDTH_PADDING = 10
|
|
10
|
+
|
|
11
|
+
def get_font_size(img):
|
|
12
|
+
width, height = img.size
|
|
13
|
+
temp_size = max(math.floor(height / 13), MINIMUM_FONT_SIZE)
|
|
14
|
+
# Scale down for very tall images to prevent text from taking over
|
|
15
|
+
if height / width >= HW_ASPECT_RATIO_THRESHOLD:
|
|
16
|
+
return math.floor(temp_size / 1.5)
|
|
17
|
+
return temp_size
|
|
18
|
+
|
|
19
|
+
def get_text_dimensions(text, font):
|
|
20
|
+
# Modern way to get text width/height in Pillow 10+
|
|
21
|
+
bbox = font.getbbox(text)
|
|
22
|
+
return bbox[2] - bbox[0], bbox[3] - bbox[1]
|
|
23
|
+
|
|
24
|
+
def get_top_left_corner(lines, font, img_width):
|
|
25
|
+
line_list = lines.split('\n')
|
|
26
|
+
# Find width of the widest line for centering
|
|
27
|
+
max_w = max(get_text_dimensions(line, font)[0] for line in line_list)
|
|
28
|
+
return ((img_width - max_w) / 2, TOP_PADDING)
|
|
29
|
+
|
|
30
|
+
def text_wrap(text, font, max_width):
|
|
31
|
+
lines = []
|
|
32
|
+
words = text.split(' ')
|
|
33
|
+
i = 0
|
|
34
|
+
while i < len(words):
|
|
35
|
+
line = ''
|
|
36
|
+
while i < len(words) and get_text_dimensions(line + words[i], font)[0] < max_width - WIDTH_PADDING:
|
|
37
|
+
line = line + words[i] + " "
|
|
38
|
+
i += 1
|
|
39
|
+
if not line:
|
|
40
|
+
line = words[i]
|
|
41
|
+
i += 1
|
|
42
|
+
lines.append(line.strip())
|
|
43
|
+
return '\n'.join(lines)
|
|
44
|
+
|
|
45
|
+
def get_white_space_height(lines, font):
|
|
46
|
+
line_list = lines.split('\n')
|
|
47
|
+
if not line_list: return 0
|
|
48
|
+
_, line_height = get_text_dimensions(line_list[0], font)
|
|
49
|
+
# Add spacing between lines (roughly 20% of line height)
|
|
50
|
+
total_text_height = len(line_list) * (line_height * 1.2)
|
|
51
|
+
return int(total_text_height + TOP_PADDING + BOTTOM_PADDING)
|
|
52
|
+
|
|
53
|
+
def get_file_name():
|
|
54
|
+
return datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dankcli_lib
|
|
3
|
+
Version: 0.5.9
|
|
4
|
+
Summary: Patched CLI Meme Generator/Caption Maker to automatically add whitespace and text to top and bottom
|
|
5
|
+
Home-page: https://github.com/TheMrRedSlime/dankcli
|
|
6
|
+
Author: TheMrRedSlime
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: dankcli dank meme memegenerator memes generator pillow dankmemes dankcli-lib caption maker make
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: pillow
|
|
12
|
+
Dynamic: author
|
|
13
|
+
Dynamic: description
|
|
14
|
+
Dynamic: description-content-type
|
|
15
|
+
Dynamic: home-page
|
|
16
|
+
Dynamic: keywords
|
|
17
|
+
Dynamic: license
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: requires-dist
|
|
20
|
+
Dynamic: summary
|
|
21
|
+
|
|
22
|
+
dankcli-lib is a CLI Image Captioning Tool, Meme Generator and Library which automatically adds white space and text to the top of your image.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
$ pip install dankcli-lib
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
$ python -m dankcli_lib "path/to/image" "Meme text you want to add" [-f "final_image_name_without_extension"]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Python:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from dankcli_lib.caption import Caption
|
|
40
|
+
|
|
41
|
+
caption = Caption("/path/to/image", "Text here", bottom_text="Bottom text here", bottom_font_color="#000000", bottom_text_box=False, font_path="arial.ttf", separator_line=True, separator_line_color="#000000", top_font_color="#ffffff", top_background_color="#000000", bottom_background_color="#000000")
|
|
42
|
+
caption.save('file.jpg')
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from dankcli_lib.caption import Caption
|
|
47
|
+
|
|
48
|
+
with Caption("image.jpg", "Your text") as caption:
|
|
49
|
+
buffer = caption.to_buffer()
|
|
50
|
+
await ctx.send(file=discord.File(buffer, "image.jpg"))
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from dankcli_lib.caption import Caption
|
|
55
|
+
import discord
|
|
56
|
+
|
|
57
|
+
caption = Caption("image.jpg", "Your text")
|
|
58
|
+
buffer = caption.to_buffer()
|
|
59
|
+
await ctx.send(file=discord.File(buffer, "image.jpg"))
|
|
60
|
+
caption.close()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
The text gets automatically wrapped according to width of image but you can also have intentional \n in your text.
|
|
65
|
+
The image is saved in the current folder with the name as the current date and time, the name can be changed with the optional `-f` or `--filename` argument, specifying a file name without the file extension.
|
|
66
|
+
|
|
67
|
+
## Example
|
|
68
|
+
|
|
69
|
+
#### Example 1 (showing \n functionality)
|
|
70
|
+
```bash
|
|
71
|
+
$ python -m dankcli_lib "templates/yesbutno.jpg" "Mom at 2am: Are you awake?\n\nMe:"
|
|
72
|
+
```
|
|
73
|
+
turns this
|
|
74
|
+
|
|
75
|
+

|
|
76
|
+
|
|
77
|
+
to this
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
#### Example 2 (showing auto textwrap)
|
|
82
|
+
```bash
|
|
83
|
+
$ python -m dankcli_lib "mymemes/helpmeme.jpg" "When you make a meme generator but now you can't stop making memes"
|
|
84
|
+
```
|
|
85
|
+
turns this
|
|
86
|
+
|
|
87
|
+

|
|
88
|
+
|
|
89
|
+
to this
|
|
90
|
+
|
|
91
|
+

|
|
92
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
dankcli_lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
dankcli_lib/__main__.py,sha256=vjc_DYzlVldMyuMao9pSsY0FhZgr_rDnH-QFs3o4X48,4679
|
|
3
|
+
dankcli_lib/caption.py,sha256=Gw-AJH-f8ZmJZ1jEMlNCJBHSbeYbQKYOyGksBu0YcYc,10039
|
|
4
|
+
dankcli_lib/functions.py,sha256=4xJLgt7J7wwHXx9PU8JTHtrYW26cxMC7ofYe2hLeCVQ,1793
|
|
5
|
+
dankcli_lib/fonts/HelveticaNeue.ttf,sha256=pRE5x00eO2aslstBe4I4pMoYZBvUUORPEEupmgFrhXw,104904
|
|
6
|
+
dankcli_lib/fonts/arial.ttf,sha256=gq-zXto6Uu2xAQa8wEr5NkY4RCHe1TjTh5LBRE2BYCI,311636
|
|
7
|
+
dankcli_lib-0.5.9.dist-info/licenses/LICENSE,sha256=36P6BuE6u6jk5eDjRVlsLsuQFHxGfWagG5XT404gTWs,1070
|
|
8
|
+
dankcli_lib-0.5.9.dist-info/METADATA,sha256=ICts1RAaHpg2HVGvYsMJljMeWXI1tKpzXNKkfZTpeKQ,2612
|
|
9
|
+
dankcli_lib-0.5.9.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
10
|
+
dankcli_lib-0.5.9.dist-info/top_level.txt,sha256=e9vV6O1assbgm4G3QNDYCMpIhhTpjYxnknp5RNhfFG4,12
|
|
11
|
+
dankcli_lib-0.5.9.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Shreyas Gupta
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dankcli_lib
|