├── .github └── workflows │ └── build_gif.yml ├── .gitignore ├── LICENSE ├── font └── Arial Unicode.ttf ├── readme.md ├── requirements-dev.txt ├── requirements.txt └── src ├── app.py ├── 臣亮言 in f 1 d 500.gif ├── 臣亮言 in f 5 d 100.gif └── 臣亮言 in f 5 d 30.gif /.github/workflows/build_gif.yml: -------------------------------------------------------------------------------- 1 | 2 | name: build gif 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | gif_text: 8 | description: 'Gif to build' 9 | required: true 10 | default: '臣亮言:先帝創業未半,而中道崩殂;今天下三分,益州疲敝,此誠危急存亡之秋也。' 11 | gif_frame: 12 | description: 'Gif frame' 13 | required: true 14 | default: '5' 15 | gif_delay: 16 | description: 'Gif delay' 17 | required: true 18 | default: '100' 19 | 20 | jobs: 21 | build-gif: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@v3 26 | 27 | - name: Set up Python 28 | uses: actions/setup-python@v3 29 | with: 30 | python-version: '3.10' 31 | 32 | - name: Install dependencies 33 | run: | 34 | python -m pip install --upgrade pip 35 | pip install -r requirements.txt 36 | 37 | - name: Build gif 38 | run: | 39 | python src/app.py -t "${{ github.event.inputs.gif_text }}" -f ${{ github.event.inputs.gif_frame }} -d ${{ github.event.inputs.gif_delay }} 40 | 41 | - name: Upload gif 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: YOUR_GIF_IS_HERE 45 | path: output.gif 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | venv/ 3 | dist/ 4 | build/ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CodingMan 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. 22 | -------------------------------------------------------------------------------- /font/Arial Unicode.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PttCodingMan/text_to_slack_gif/dc0da061e7052489daad5e1623c1718b3e52866a/font/Arial Unicode.ttf -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # text to slack gif 2 | 3 | This is a simple Python script that can convert text to gif in slack format. 4 | This script solves the problem that you need to use several online services to do this. 5 | And the more important thing is it saves your time! 6 | 7 | ## Basic usage 8 | 9 | ### Run on GitHub Actions 10 | 11 | 1. Fork this repository. 12 | 2. Go to the Actions tab and enable workflows. 13 | 3. Select `build gif` workflow. 14 | 4. Click `Run workflow` button. 15 | 5. Input the text you want to convert. 16 | 6. Download the gif from the Artifacts. 17 | 18 | ### Run on your local machine 19 | 20 | 1. Clone this repository. 21 | 2. Install the required packages. 22 | 3. Run the script. 23 | 24 | ```bash 25 | python3 src/app.py -t "臣亮言..." 26 | ``` 27 | 28 | ## Advanced usage 29 | 30 | However, you can get different type gif by adjusting frame and delay. 31 | The default values for frame and delay are 5 and 100 respectively. 32 | 33 | ```bash 34 | python3 src/app.py -t "臣亮言..." 35 | ``` 36 | ![image](https://raw.githubusercontent.com/PttCodingMan/text_to_slack_gif/master/src/%E8%87%A3%E4%BA%AE%E8%A8%80%20in%20f%205%20d%20100.gif) 37 | 38 | ```bash 39 | python3 src/app.py -f 1 -d 500 -t "臣亮言..." 40 | ``` 41 | ![image](https://raw.githubusercontent.com/PttCodingMan/text_to_slack_gif/master/src/%E8%87%A3%E4%BA%AE%E8%A8%80%20in%20f%201%20d%20500.gif) 42 | 43 | ```bash 44 | python3 src/app.py -d 30 -t "臣亮言..." 45 | ``` 46 | ![image](https://raw.githubusercontent.com/PttCodingMan/text_to_slack_gif/master/src/%E8%87%A3%E4%BA%AE%E8%A8%80%20in%20f%205%20d%2030.gif) 47 | 48 | ## Theme 49 | 50 | Add `--theme ` argument to change the theme of the gif. Default is `dark`. 51 | 52 | - `dark`: Black background with white text. 53 | - `light`: White background with black text. 54 | 55 | ```bash 56 | python3 src/app.py --theme light -t "臣亮言..." 57 | ``` 58 | 59 | ## Font color 60 | 61 | Add `-c ` or `--color=` argument to change font color; type=str. This will override the default color of the selected theme. 62 | 63 | ```bash 64 | python3 src/app.py -c blueviolet -t "臣亮言..." 65 | ``` 66 | ![image](https://github.com/PersonalComputerRetailer/text_to_slack_gif/blob/dev/src/%E8%87%A3%E4%BA%AE%E8%A8%80%EF%BC%9A%E5%85%88%20in%20f%205%20d%20100_violet.gif) 67 | 68 | ## Width of image 69 | 70 | Add `-w ` or `--width=` argument to change width of image; type=int. Default is 1. 71 | 72 | ```bash 73 | python3 src/app.py -c blueviolet -w 5 -t "臣亮言..." 74 | ``` 75 | ![image](https://github.com/PersonalComputerRetailer/text_to_slack_gif/blob/dev/src/%E8%87%A3%E4%BA%AE%E8%A8%80%EF%BC%9A%E5%85%88%20in%20f%205%20d%20100_violet_w5.gif) 76 | 77 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | SingleLog 2 | Pillow 3 | Pyinstaller -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | SingleLog 2 | Pillow -------------------------------------------------------------------------------- /src/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import string 3 | from argparse import ArgumentParser, ArgumentTypeError 4 | 5 | from PIL import ImageFont, Image, ImageDraw 6 | from SingleLog import Logger 7 | 8 | # default value 9 | default_frame = 5 10 | default_delay = 100 11 | image_size = 50 12 | y_offset = -10 13 | default_color = 'black' 14 | default_width = 1 15 | 16 | logger = Logger('app') 17 | 18 | _script_path = os.path.dirname(os.path.abspath(__file__)) 19 | 20 | 21 | def check_positive(value): 22 | value = int(value) 23 | if value <= 0: 24 | raise ArgumentTypeError("%s is an invalid positive int value" % value) 25 | return value 26 | 27 | 28 | def new_image(width, background_color): 29 | return Image.new('RGB', (image_size * width, image_size), background_color) 30 | 31 | 32 | def text_to_gif(text: str, frame: int, delay: int, font: str, save: bool, text_color: str, width: int, background_color: str): 33 | input_string = ' '.join(text.split()) 34 | 35 | if font is None: 36 | font = f'{_script_path}/../font/Arial Unicode.ttf' 37 | 38 | logger.debug('text', input_string) 39 | logger.debug('frame', frame) 40 | logger.debug('delay', delay) 41 | 42 | single_full_text_length = image_size 43 | # load font 44 | font = ImageFont.truetype(font, image_size, index=0) 45 | 46 | img = new_image(width, background_color) 47 | d = ImageDraw.Draw(img) 48 | 49 | if frame == 1: 50 | images = [] 51 | for i, text in enumerate(input_string): 52 | 53 | logger.debug('text', text) 54 | if text in string.whitespace: 55 | logger.debug('pass') 56 | continue 57 | 58 | # create image with white 59 | img = new_image(width, background_color) 60 | d = ImageDraw.Draw(img) 61 | 62 | if text in string.ascii_letters: 63 | w = d.textlength(input_string, font=font) 64 | start_x = int((image_size - w) / 2) 65 | else: 66 | start_x = 0 67 | 68 | logger.debug('start x', start_x) 69 | # draw text in image 70 | d.text((start_x, y_offset), text, fill=text_color, font=font) 71 | images.append(img) 72 | else: 73 | text_total_width = d.textlength(input_string, font=font) 74 | logger.debug('text_total_width', text_total_width) 75 | 76 | frame_offset = single_full_text_length // frame 77 | logger.debug('frame_offset', frame_offset) 78 | 79 | x = (frame - 1) * frame_offset * width 80 | images = [] 81 | while (text_total_width + x) >= frame_offset: 82 | img = new_image(width, background_color) 83 | d = ImageDraw.Draw(img) 84 | 85 | d.text((x, y_offset), input_string, fill=text_color, font=font) 86 | 87 | images.append(img) 88 | x -= frame_offset 89 | 90 | for _ in range((frame - 1) * width): 91 | images.append(images.pop(0)) 92 | 93 | if save: 94 | # output_name = f'{input_string[:5].strip()} in f {frame} d {delay}.gif' 95 | output_name = "output.gif" 96 | images[0].save( 97 | fp=output_name, format='GIF', append_images=images[1:], save_all=True, 98 | duration=delay, 99 | optimize=True, 100 | loop=0) 101 | 102 | logger.info(output_name, 'generated') 103 | 104 | return images 105 | 106 | 107 | if __name__ == '__main__': 108 | parser = ArgumentParser() 109 | parser.add_argument('-t', '--text', help="Any text you want to convert to gif", required=True) 110 | parser.add_argument('-f', '--frame', type=check_positive, default=default_frame, 111 | help="Frames number for each text between text") 112 | parser.add_argument('-d', '--delay', type=check_positive, default=default_delay, help="The delay for each frame") 113 | parser.add_argument('-c', '--color', type=str, default=None, 114 | help="The text HTML/CSS Color Name; string, default: black") 115 | parser.add_argument('-w', '--width', type=int, default=default_width, 116 | help="The width of image; integer; default: 1") 117 | parser.add_argument('--theme', type=str, default='dark', choices=['dark', 'light'], 118 | help="The theme of the gif; string, default: dark") 119 | args = parser.parse_args() 120 | 121 | if args.theme == 'dark': 122 | background_color = 'black' 123 | text_color = 'white' 124 | else: 125 | background_color = 'white' 126 | text_color = 'black' 127 | 128 | if args.color: 129 | text_color = args.color 130 | 131 | text_to_gif(args.text, args.frame, args.delay, None, True, text_color, args.width, background_color) 132 | -------------------------------------------------------------------------------- /src/臣亮言 in f 1 d 500.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PttCodingMan/text_to_slack_gif/dc0da061e7052489daad5e1623c1718b3e52866a/src/臣亮言 in f 1 d 500.gif -------------------------------------------------------------------------------- /src/臣亮言 in f 5 d 100.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PttCodingMan/text_to_slack_gif/dc0da061e7052489daad5e1623c1718b3e52866a/src/臣亮言 in f 5 d 100.gif -------------------------------------------------------------------------------- /src/臣亮言 in f 5 d 30.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PttCodingMan/text_to_slack_gif/dc0da061e7052489daad5e1623c1718b3e52866a/src/臣亮言 in f 5 d 30.gif --------------------------------------------------------------------------------