├── .env ├── .gitignore ├── README.md ├── fonts ├── Roboto-Black.ttf ├── Roboto-BlackItalic.ttf ├── Roboto-Bold.ttf ├── Roboto-BoldItalic.ttf ├── Roboto-Italic.ttf ├── Roboto-Light.ttf ├── Roboto-LightItalic.ttf ├── Roboto-Medium.ttf ├── Roboto-MediumItalic.ttf ├── Roboto-Regular.ttf ├── Roboto-Thin.ttf └── Roboto-ThinItalic.ttf ├── main.py ├── quotes.json └── requirements.txt /.env: -------------------------------------------------------------------------------- 1 | # Images to generate with 1 command 2 | IMAGES_TO_GENERATE=10 3 | # The resolution of the images, higher is better, but slower. It needs to be in the form of WIDTHxHEIGHT 4 | RESOLUTION=4000x4000 5 | # Category of the background images 6 | CATEGORY=nature 7 | # Font size of the text, needs to be a number. Remember that the higher the resolution, the higher the font size needs to be. 8 | FONT_SIZE=150 9 | # Save the quotes to a file so there are no duplicates. Only True and False are allowed. 10 | SAVE_QUOTES_TO_FILE=True -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Include 2 | /Lib 3 | /Scripts 4 | pyvenv.cfg 5 | /images -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageQuoteGenerator 2 | This is project is written in Python. It uses the Pillow library to create images with text on them. The quotes are from the [Quotable API](https://quotable.io/). The images are from [Unsplash](https://unsplash.com/). 3 | ## Example 4 | ![quote_0](https://user-images.githubusercontent.com/65854503/192774490-ba3e4139-c4ae-428f-b301-ba3c337761c4.jpg) 5 | -------------------------------------------------------------------------------- /fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceSimon/ImageQuoteGenerator/e5f672db9e98a1eb44c457ad2b23b0bb126f4dcb/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import textwrap 4 | from datetime import datetime 5 | 6 | import requests 7 | from PIL import Image, ImageDraw, ImageFont 8 | from dotenv import load_dotenv 9 | 10 | 11 | def import_env(): 12 | load_dotenv() 13 | IMAGES_TO_GENERATE = os.getenv('IMAGES_TO_GENERATE') 14 | if IMAGES_TO_GENERATE is None or IMAGES_TO_GENERATE == '': 15 | IMAGES_TO_GENERATE = 1 16 | return IMAGES_TO_GENERATE 17 | 18 | 19 | def draw_multiple_line_text(image, text, font, text_color, text_start_height): 20 | draw = ImageDraw.Draw(image) 21 | image_width, image_height = image.size 22 | y_text = text_start_height 23 | lines = textwrap.wrap(text, width=45) 24 | for line in lines: 25 | nothing1, nothing2, line_width, line_height = font.getbbox(line) 26 | # draw shadow on text 27 | draw.text(((image_width - line_width) / 2 + 2, y_text + 2), 28 | line, font=font, fill=(0, 0, 0)) 29 | draw.text(((image_width - line_width) / 2, y_text), 30 | line, font=font, fill=text_color) 31 | y_text += line_height 32 | # Return the bottom pixel of the text 33 | return y_text 34 | 35 | 36 | def get_quote(): 37 | try: 38 | data = requests.get('https://api.quotable.io/random').json() 39 | quote = data['content'] 40 | author = data['author'] 41 | SAVE_QUOTES_TO_FILE = os.getenv('SAVE_QUOTES_TO_FILE') 42 | quotes = open('quotes.json', 'r') 43 | if quote in quotes and SAVE_QUOTES_TO_FILE == 'True': 44 | print("\033[91m" + "Error: Quote already exists! \033[0m") 45 | get_quote() 46 | elif SAVE_QUOTES_TO_FILE == 'True': 47 | with open('quotes.json', 'r') as f: 48 | data = json.load(f) 49 | data.append({ 50 | 'quote': quote, 51 | 'author': author 52 | }) 53 | with open('quotes.json', 'w') as f: 54 | json.dump(data, f) 55 | else: 56 | pass 57 | print("\033[92m" + "Quote found! \033[0m") 58 | print("\033[92m" + quote + " - " + author + "\033[0m") 59 | return quote, author 60 | except KeyError: 61 | print("\033[91m" + "Error: Quote not found! \033[0m") 62 | get_quote() 63 | 64 | 65 | def create_image(quote, author, i, folder): 66 | try: 67 | resolution = os.getenv('RESOLUTION') 68 | category = os.getenv('CATEGORY') 69 | font_size = os.getenv('FONT_SIZE') 70 | if resolution is None or resolution == '' or not 'x' in resolution: 71 | resolution = (1080, 1080) 72 | image = Image.open(requests.get(f'https://source.unsplash.com/random/{resolution}?{category}', stream=True).raw) 73 | image = Image.open(requests.get(f'https://source.unsplash.com/random/{resolution}?{category}', 74 | stream=True).raw) # Two times because the first one is always the same 75 | # The image not found image of Unsplash is 1200x800. 76 | if image.size == (1200, 800): 77 | print("\033[91m" + "Error: Image not found! \033[0m") 78 | return None 79 | image = image.resize((4000, 4000)) 80 | draw = ImageDraw.Draw(image) 81 | font = ImageFont.truetype('fonts/Roboto-Italic.ttf', int(font_size)) 82 | font2 = ImageFont.truetype('fonts/Roboto-Bold.ttf', int(font_size)) 83 | if len(quote) > 350: 84 | text_start_height = (image.height - font.getbbox(quote)[3]) / 2 - 500 85 | elif len(quote) > 250: 86 | text_start_height = (image.height - font.getbbox(quote)[3]) / 2 - 200 87 | elif len(quote) > 150: 88 | text_start_height = (image.height - font.getbbox(quote)[3]) / 2 - 50 89 | else: 90 | text_start_height = (image.height - font.getbbox(quote)[3]) / 2 91 | end = draw_multiple_line_text(image, quote, font, text_color=(255, 255, 255), 92 | text_start_height=text_start_height) 93 | # Draw the author shadow 94 | draw.text(((image.width - font2.getbbox(author)[2]) / 2 + 2, end + 50), 95 | author, font=font2, fill=(0, 0, 0)) 96 | # Draw the author 97 | draw.text(((image.width - font2.getbbox(author)[2]) / 2, end + 50), author, font=font2, fill=(255, 255, 255)) 98 | # If file already exists, add a number to the end of the file. Check again if the file exists and add a number to the end of the file. 99 | for i in range(100): 100 | if os.path.exists(f'{folder}/{i}.jpg'): 101 | continue 102 | else: 103 | image.save(f'{folder}/{i}.jpg') 104 | break 105 | print(i) 106 | print("\033[92m" + "Image created! \033[0m") 107 | except KeyError: 108 | print("\033[91m" + "Error: Image not created! \033[0m") 109 | return None 110 | 111 | 112 | def create_folder(): 113 | # Get the date 114 | now = datetime.now() 115 | date = now.strftime("%d-%m-%Y") 116 | hour = now.strftime("%H") 117 | if not os.path.exists(f'images/{date}/{hour}h - {os.getenv("CATEGORY")}'): 118 | os.makedirs(f'images/{date}/{hour}h - {os.getenv("CATEGORY")}') 119 | return f'images/{date}/{hour}h - {os.getenv("CATEGORY")}' 120 | 121 | 122 | if __name__ == '__main__': 123 | print("\033[92m" + "Starting... \033[0m") 124 | IMAGES_TO_GENERATE = import_env() 125 | print("\033[92m" + "Environment variables imported! \033[0m") 126 | folder = create_folder() 127 | for i in range(int(IMAGES_TO_GENERATE)): 128 | quote, author = get_quote() 129 | create_image(quote, author, i, folder) 130 | -------------------------------------------------------------------------------- /quotes.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.32.2 2 | Pillow==10.3.0 3 | python-dotenv~=0.21.0 4 | --------------------------------------------------------------------------------