├── .gitignore
├── LICENSE
├── README.md
├── assets
├── fonts
│ ├── LuckiestGuy-Regular.ttf
│ └── README.md
└── images
│ ├── background.png
│ ├── box.psd
│ ├── box_bottom_common.png
│ ├── box_bottom_dark.png
│ ├── box_bottom_dc.png
│ ├── box_bottom_epic.png
│ ├── box_bottom_frozen.png
│ ├── box_bottom_icon.png
│ ├── box_bottom_lava.png
│ ├── box_bottom_legendary.png
│ ├── box_bottom_marvel.png
│ ├── box_bottom_rare.png
│ ├── box_bottom_shadow.png
│ ├── box_bottom_starwars.png
│ ├── box_bottom_uncommon.png
│ ├── box_faceplate_common.png
│ ├── box_faceplate_dark.png
│ ├── box_faceplate_dc.png
│ ├── box_faceplate_epic.png
│ ├── box_faceplate_frozen.png
│ ├── box_faceplate_icon.png
│ ├── box_faceplate_lava.png
│ ├── box_faceplate_legendary.png
│ ├── box_faceplate_marvel.png
│ ├── box_faceplate_rare.png
│ ├── box_faceplate_shadow.png
│ ├── box_faceplate_starwars.png
│ ├── box_faceplate_uncommon.png
│ ├── card.psd
│ ├── card_bottom_common.png
│ ├── card_bottom_dark.png
│ ├── card_bottom_dc.png
│ ├── card_bottom_epic.png
│ ├── card_bottom_frozen.png
│ ├── card_bottom_icon.png
│ ├── card_bottom_lava.png
│ ├── card_bottom_legendary.png
│ ├── card_bottom_marvel.png
│ ├── card_bottom_rare.png
│ ├── card_bottom_shadow.png
│ ├── card_bottom_starwars.png
│ ├── card_bottom_uncommon.png
│ ├── card_faceplate_common.png
│ ├── card_faceplate_dark.png
│ ├── card_faceplate_dc.png
│ ├── card_faceplate_epic.png
│ ├── card_faceplate_frozen.png
│ ├── card_faceplate_icon.png
│ ├── card_faceplate_lava.png
│ ├── card_faceplate_legendary.png
│ ├── card_faceplate_marvel.png
│ ├── card_faceplate_rare.png
│ ├── card_faceplate_shadow.png
│ ├── card_faceplate_starwars.png
│ ├── card_faceplate_uncommon.png
│ ├── card_top_common.png
│ ├── card_top_dark.png
│ ├── card_top_dc.png
│ ├── card_top_epic.png
│ ├── card_top_frozen.png
│ ├── card_top_icon.png
│ ├── card_top_lava.png
│ ├── card_top_legendary.png
│ ├── card_top_marvel.png
│ ├── card_top_rare.png
│ ├── card_top_shadow.png
│ ├── card_top_starwars.png
│ ├── card_top_uncommon.png
│ ├── logo.png
│ └── vbucks.png
├── configuration_example.json
├── itemshop.py
└── util.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
106 | # Visual Studio Code
107 | .vscode/
108 |
109 | # Burbank font (paid)
110 | # https://fonts.adobe.com/fonts/burbank
111 | assets/fonts/BurbankBigCondensed-Black.otf
112 |
113 | # Output
114 | itemshop.png
115 |
116 | # Credentials
117 | configuration.json
118 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Ethan
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Athena
2 |
3 | Athena is a utility which generates the current Fortnite Item Shop into a stylized image and shares it on Twitter.
4 |
5 | As seen on [@FNMasterCom](https://twitter.com/FNMasterCom/status/1197666123078160386?s=20)...
6 |
7 |
8 |
9 |
10 |
11 | ## Requirements
12 |
13 | - [Python 3.7](https://www.python.org/downloads/)
14 | - [Requests](http://docs.python-requests.org/en/master/user/install/)
15 | - [coloredlogs](https://pypi.org/project/coloredlogs/)
16 | - [Pillow](https://pillow.readthedocs.io/en/stable/installation.html#basic-installation)
17 | - [python-twitter](https://github.com/bear/python-twitter#installing)
18 |
19 | A [Fortnite-API API Key](https://fortnite-api.com/profile) is required to obtain the Item Shop data, [Twitter API credentials](https://developer.twitter.com/en/apps) are required to Tweet the image.
20 |
21 | ## Usage
22 |
23 | Open `configuration_example.json` in your preferred text editor, fill the configurable values. Once finished, save and rename the file to `configuration.json`.
24 |
25 | - `delayStart`: Set to `0` to begin the process immediately
26 | - `language`: Set the language for the Item Shop data ([Supported Languages](https://fortnite-api.com/documentation))
27 | - `supportACreator`: Leave blank to omit the Support-A-Creator tag section of the Tweet
28 | - `twitter`: Set `enabled` to `false` if you wish for `itemshop.png` to not be Tweeted
29 |
30 | Edit the images found in `assets/images/` to your liking, avoid changing image dimensions for optimal results.
31 |
32 | Athena is designed to be ran using a scheduler, such as [cron](https://en.wikipedia.org/wiki/Cron).
33 |
34 | ```
35 | python itemshop.py
36 | ```
37 |
38 | ## Credits
39 |
40 | - Item Shop data provided by [Fortnite-API](https://fortnite-api.com/)
41 | - Burbank font property of [Adobe](https://fonts.adobe.com/fonts/burbank)
42 | - Luckiest Guy font property of [Google](https://fonts.google.com/specimen/Luckiest+Guy)
43 |
--------------------------------------------------------------------------------
/assets/fonts/LuckiestGuy-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/fonts/LuckiestGuy-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/README.md:
--------------------------------------------------------------------------------
1 | # Important
2 |
3 | You will need to *obtain* a copy of `BurbankBigCondensed-Black.otf` for optimal results! This is the font which Epic Games uses for Fortnite assets.
4 |
5 | If `BurbankBigCondensed-Black.otf` is not found, the font used will default to [Luckiest Guy](https://fonts.google.com/specimen/Luckiest+Guy).
6 |
7 | ## Purchase Burbank
8 |
9 | - Adobe: https://fonts.adobe.com/fonts/burbank
10 |
--------------------------------------------------------------------------------
/assets/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/background.png
--------------------------------------------------------------------------------
/assets/images/box.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box.psd
--------------------------------------------------------------------------------
/assets/images/box_bottom_common.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_common.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_dark.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_dc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_dc.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_epic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_epic.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_frozen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_frozen.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_icon.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_lava.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_lava.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_legendary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_legendary.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_marvel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_marvel.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_rare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_rare.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_shadow.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_starwars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_starwars.png
--------------------------------------------------------------------------------
/assets/images/box_bottom_uncommon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_bottom_uncommon.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_common.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_common.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_dark.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_dc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_dc.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_epic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_epic.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_frozen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_frozen.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_icon.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_lava.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_lava.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_legendary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_legendary.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_marvel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_marvel.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_rare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_rare.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_shadow.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_starwars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_starwars.png
--------------------------------------------------------------------------------
/assets/images/box_faceplate_uncommon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/box_faceplate_uncommon.png
--------------------------------------------------------------------------------
/assets/images/card.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card.psd
--------------------------------------------------------------------------------
/assets/images/card_bottom_common.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_common.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_dark.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_dc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_dc.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_epic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_epic.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_frozen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_frozen.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_icon.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_lava.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_lava.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_legendary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_legendary.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_marvel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_marvel.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_rare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_rare.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_shadow.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_starwars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_starwars.png
--------------------------------------------------------------------------------
/assets/images/card_bottom_uncommon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_bottom_uncommon.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_common.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_common.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_dark.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_dc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_dc.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_epic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_epic.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_frozen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_frozen.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_icon.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_lava.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_lava.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_legendary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_legendary.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_marvel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_marvel.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_rare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_rare.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_shadow.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_starwars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_starwars.png
--------------------------------------------------------------------------------
/assets/images/card_faceplate_uncommon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_faceplate_uncommon.png
--------------------------------------------------------------------------------
/assets/images/card_top_common.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_common.png
--------------------------------------------------------------------------------
/assets/images/card_top_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_dark.png
--------------------------------------------------------------------------------
/assets/images/card_top_dc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_dc.png
--------------------------------------------------------------------------------
/assets/images/card_top_epic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_epic.png
--------------------------------------------------------------------------------
/assets/images/card_top_frozen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_frozen.png
--------------------------------------------------------------------------------
/assets/images/card_top_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_icon.png
--------------------------------------------------------------------------------
/assets/images/card_top_lava.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_lava.png
--------------------------------------------------------------------------------
/assets/images/card_top_legendary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_legendary.png
--------------------------------------------------------------------------------
/assets/images/card_top_marvel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_marvel.png
--------------------------------------------------------------------------------
/assets/images/card_top_rare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_rare.png
--------------------------------------------------------------------------------
/assets/images/card_top_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_shadow.png
--------------------------------------------------------------------------------
/assets/images/card_top_starwars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_starwars.png
--------------------------------------------------------------------------------
/assets/images/card_top_uncommon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/card_top_uncommon.png
--------------------------------------------------------------------------------
/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/logo.png
--------------------------------------------------------------------------------
/assets/images/vbucks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthanC/Athena/7eceb0bb1e31d51242161f62f8b70973fecb03e4/assets/images/vbucks.png
--------------------------------------------------------------------------------
/configuration_example.json:
--------------------------------------------------------------------------------
1 | {
2 | "delayStart": 10,
3 | "fortniteAPI": {
4 | "apiKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
5 | },
6 | "language": "en",
7 | "supportACreator": "YourSupportACreatorCode",
8 | "twitter": {
9 | "enabled": true,
10 | "apiKey": "XXXXXXXXXX",
11 | "apiSecret": "XXXXXXXXXX",
12 | "accessToken": "XXXXXXXXXX",
13 | "accessSecret": "XXXXXXXXXX"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/itemshop.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | from math import ceil
4 | from sys import exit
5 | from time import sleep
6 |
7 | import coloredlogs
8 | import twitter
9 | from PIL import Image, ImageDraw
10 |
11 | from util import ImageUtil, Utility
12 |
13 | log = logging.getLogger(__name__)
14 | coloredlogs.install(level="INFO", fmt="[%(asctime)s] %(message)s", datefmt="%I:%M:%S")
15 |
16 |
17 | class Athena:
18 | """Fortnite Item Shop Generator."""
19 |
20 | def main(self):
21 | print("Athena - Fortnite Item Shop Generator")
22 | print("https://github.com/EthanC/Athena\n")
23 |
24 | initialized = Athena.LoadConfiguration(self)
25 |
26 | if initialized is True:
27 | if self.delay > 0:
28 | log.info(f"Delaying process start for {self.delay}s...")
29 | sleep(self.delay)
30 |
31 | itemShop = Utility.GET(
32 | self,
33 | "https://fortnite-api.com/shop/br",
34 | {"x-api-key": self.apiKey},
35 | {"language": self.language},
36 | )
37 |
38 | if itemShop is not None:
39 | itemShop = json.loads(itemShop)["data"]
40 |
41 | # Strip time from the timestamp, we only need the date
42 | date = Utility.ISOtoHuman(
43 | self, itemShop["date"].split("T")[0], self.language
44 | )
45 | log.info(f"Retrieved Item Shop for {date}")
46 |
47 | shopImage = Athena.GenerateImage(self, date, itemShop)
48 |
49 | if shopImage is True:
50 | if self.twitterEnabled is True:
51 | Athena.Tweet(self, date)
52 |
53 | def LoadConfiguration(self):
54 | """
55 | Set the configuration values specified in configuration.json
56 |
57 | Return True if configuration sucessfully loaded.
58 | """
59 |
60 | configuration = json.loads(Utility.ReadFile(self, "configuration", "json"))
61 |
62 | try:
63 | self.delay = configuration["delayStart"]
64 | self.apiKey = configuration["fortniteAPI"]["apiKey"]
65 | self.language = configuration["language"]
66 | self.supportACreator = configuration["supportACreator"]
67 | self.twitterEnabled = configuration["twitter"]["enabled"]
68 | self.twitterAPIKey = configuration["twitter"]["apiKey"]
69 | self.twitterAPISecret = configuration["twitter"]["apiSecret"]
70 | self.twitterAccessToken = configuration["twitter"]["accessToken"]
71 | self.twitterAccessSecret = configuration["twitter"]["accessSecret"]
72 |
73 | log.info("Loaded configuration")
74 |
75 | return True
76 | except Exception as e:
77 | log.critical(f"Failed to load configuration, {e}")
78 |
79 | def GenerateImage(self, date: str, itemShop: dict):
80 | """
81 | Generate the Item Shop image using the provided Item Shop.
82 |
83 | Return True if image sucessfully saved.
84 | """
85 |
86 | try:
87 | featured = itemShop["featured"]
88 | daily = itemShop["daily"]
89 |
90 | # Ensure both Featured and Daily have at least 1 item
91 | if (len(featured) <= 0) or (len(daily) <= 0):
92 | raise Exception(f"Featured: {len(featured)}, Daily: {len(daily)}")
93 | except Exception as e:
94 | log.critical(f"Failed to parse Item Shop Featured and Daily items, {e}")
95 |
96 | return False
97 |
98 | # Determine the max amount of rows required for the current
99 | # Item Shop when there are 3 columns for both Featured and Daily.
100 | # This allows us to determine the image height.
101 | rows = max(ceil(len(featured) / 3), ceil(len(daily) / 3))
102 | shopImage = Image.new("RGB", (1920, ((545 * rows) + 340)))
103 |
104 | try:
105 | background = ImageUtil.Open(self, "background.png")
106 | background = ImageUtil.RatioResize(
107 | self, background, shopImage.width, shopImage.height
108 | )
109 | shopImage.paste(
110 | background, ImageUtil.CenterX(self, background.width, shopImage.width)
111 | )
112 | except FileNotFoundError:
113 | log.warn("Failed to open background.png, defaulting to dark gray")
114 | shopImage.paste((18, 18, 18), [0, 0, shopImage.size[0], shopImage.size[1]])
115 |
116 | logo = ImageUtil.Open(self, "logo.png")
117 | logo = ImageUtil.RatioResize(self, logo, 0, 210)
118 | shopImage.paste(
119 | logo, ImageUtil.CenterX(self, logo.width, shopImage.width, 20), logo
120 | )
121 |
122 | canvas = ImageDraw.Draw(shopImage)
123 | font = ImageUtil.Font(self, 48)
124 | textWidth, _ = font.getsize(date)
125 | canvas.text(
126 | ImageUtil.CenterX(self, textWidth, shopImage.width, 255),
127 | date,
128 | (255, 255, 255),
129 | font=font,
130 | )
131 | canvas.text((20, 255), "Featured", (255, 255, 255), font=font)
132 | textWidth, _ = font.getsize("Daily")
133 | canvas.text(
134 | (shopImage.width - (textWidth + 20), 255),
135 | "Daily",
136 | (255, 255, 255),
137 | font=font,
138 | )
139 |
140 | # Track grid position
141 | i = 0
142 |
143 | for item in featured:
144 | card = Athena.GenerateCard(self, item)
145 |
146 | if card is not None:
147 | shopImage.paste(
148 | card,
149 | (
150 | (20 + ((i % 3) * (card.width + 5))),
151 | (315 + ((i // 3) * (card.height + 5))),
152 | ),
153 | card,
154 | )
155 |
156 | i += 1
157 |
158 | # Reset grid position
159 | i = 0
160 |
161 | for item in daily:
162 | card = Athena.GenerateCard(self, item)
163 |
164 | if card is not None:
165 | shopImage.paste(
166 | card,
167 | (
168 | (990 + ((i % 3) * (card.width + 5))),
169 | (315 + ((i // 3) * (card.height + 5))),
170 | ),
171 | card,
172 | )
173 |
174 | i += 1
175 |
176 | try:
177 | shopImage.save("itemshop.png")
178 | log.info("Generated Item Shop image")
179 |
180 | return True
181 | except Exception as e:
182 | log.critical(f"Failed to save Item Shop image, {e}")
183 |
184 | def GenerateCard(self, item: dict):
185 | """Return the card image for the provided Fortnite Item Shop item."""
186 |
187 | try:
188 | name = item["items"][0]["name"]
189 | rarity = item["items"][0]["rarity"]
190 | category = item["items"][0]["type"]
191 | price = item["finalPrice"]
192 | if isinstance(item["items"][0]["images"]["featured"], dict):
193 | icon = item["items"][0]["images"]["featured"]["url"]
194 | else:
195 | icon = item["items"][0]["images"]["icon"]["url"]
196 | except Exception as e:
197 | log.error(f"Failed to parse item {name}, {e}")
198 |
199 | return
200 |
201 | if rarity == "frozen":
202 | blendColor = (148, 223, 255)
203 | elif rarity == "lava":
204 | blendColor = (234, 141, 35)
205 | elif rarity == "legendary":
206 | blendColor = (211, 120, 65)
207 | elif rarity == "dark":
208 | blendColor = (251, 34, 223)
209 | elif rarity == "starwars":
210 | blendColor = (231, 196, 19)
211 | elif rarity == "marvel":
212 | blendColor = (197, 51, 52)
213 | elif rarity == "dc":
214 | blendColor = (84, 117, 199)
215 | elif rarity == "icon":
216 | blendColor = (54, 183, 183)
217 | elif rarity == "shadow":
218 | blendColor = (113, 113, 113)
219 | elif rarity == "epic":
220 | blendColor = (177, 91, 226)
221 | elif rarity == "rare":
222 | blendColor = (73, 172, 242)
223 | elif rarity == "uncommon":
224 | blendColor = (96, 170, 58)
225 | elif rarity == "common":
226 | blendColor = (190, 190, 190)
227 | else:
228 | blendColor = (255, 255, 255)
229 |
230 | card = Image.new("RGBA", (300, 545))
231 |
232 | try:
233 | layer = ImageUtil.Open(self, f"card_top_{rarity}.png")
234 | except FileNotFoundError:
235 | log.warn(f"Failed to open card_top_{rarity}.png, defaulted to Common")
236 | layer = ImageUtil.Open(self, "card_top_common.png")
237 |
238 | card.paste(layer)
239 |
240 | icon = ImageUtil.Download(self, icon)
241 | if (category == "outfit") or (category == "emote"):
242 | icon = ImageUtil.RatioResize(self, icon, 285, 365)
243 | elif category == "wrap":
244 | icon = ImageUtil.RatioResize(self, icon, 230, 310)
245 | else:
246 | icon = ImageUtil.RatioResize(self, icon, 310, 390)
247 | if (category == "outfit") or (category == "emote"):
248 | card.paste(icon, ImageUtil.CenterX(self, icon.width, card.width), icon)
249 | else:
250 | card.paste(icon, ImageUtil.CenterX(self, icon.width, card.width, 15), icon)
251 |
252 | if len(item["items"]) > 1:
253 | # Track grid position
254 | i = 0
255 |
256 | # Start at position 1 in items array
257 | for extra in item["items"][1:]:
258 | try:
259 | extraRarity = extra["rarity"]
260 | extraIcon = extra["images"]["smallIcon"]["url"]
261 | except Exception as e:
262 | log.error(f"Failed to parse item {name}, {e}")
263 |
264 | return
265 |
266 | try:
267 | layer = ImageUtil.Open(self, f"box_bottom_{extraRarity}.png")
268 | except FileNotFoundError:
269 | log.warn(
270 | f"Failed to open box_bottom_{extraRarity}.png, defaulted to Common"
271 | )
272 | layer = ImageUtil.Open(self, "box_bottom_common.png")
273 |
274 | card.paste(
275 | layer,
276 | (
277 | (card.width - (layer.width + 9)),
278 | (9 + ((i // 1) * (layer.height))),
279 | ),
280 | )
281 |
282 | extraIcon = ImageUtil.Download(self, extraIcon)
283 | extraIcon = ImageUtil.RatioResize(self, extraIcon, 75, 75)
284 |
285 | card.paste(
286 | extraIcon,
287 | (
288 | (card.width - (layer.width + 9)),
289 | (9 + ((i // 1) * (extraIcon.height))),
290 | ),
291 | extraIcon,
292 | )
293 |
294 | try:
295 | layer = ImageUtil.Open(self, f"box_faceplate_{extraRarity}.png")
296 | except FileNotFoundError:
297 | log.warn(
298 | f"Failed to open box_faceplate_{extraRarity}.png, defaulted to Common"
299 | )
300 | layer = ImageUtil.Open(self, "box_faceplate_common.png")
301 |
302 | card.paste(
303 | layer,
304 | (
305 | (card.width - (layer.width + 9)),
306 | (9 + ((i // 1) * (layer.height))),
307 | ),
308 | layer,
309 | )
310 |
311 | i += 1
312 |
313 | try:
314 | layer = ImageUtil.Open(self, f"card_faceplate_{rarity}.png")
315 | except FileNotFoundError:
316 | log.warn(f"Failed to open card_faceplate_{rarity}.png, defaulted to Common")
317 | layer = ImageUtil.Open(self, "card_faceplate_common.png")
318 |
319 | card.paste(layer, layer)
320 |
321 | try:
322 | layer = ImageUtil.Open(self, f"card_bottom_{rarity}.png")
323 | except FileNotFoundError:
324 | log.warn(f"Failed to open card_bottom_{rarity}.png, defaulted to Common")
325 | layer = ImageUtil.Open(self, "card_bottom_common.png")
326 |
327 | card.paste(layer, layer)
328 |
329 | canvas = ImageDraw.Draw(card)
330 |
331 | font = ImageUtil.Font(self, 30)
332 | textWidth, _ = font.getsize(f"{rarity.capitalize()} {category.capitalize()}")
333 | canvas.text(
334 | ImageUtil.CenterX(self, textWidth, card.width, 385),
335 | f"{rarity.capitalize()} {category.capitalize()}",
336 | blendColor,
337 | font=font,
338 | )
339 |
340 | vbucks = ImageUtil.Open(self, "vbucks.png")
341 | vbucks = ImageUtil.RatioResize(self, vbucks, 25, 25)
342 |
343 | price = str(f"{price:,}")
344 | textWidth, _ = font.getsize(price)
345 | canvas.text(
346 | ImageUtil.CenterX(self, ((textWidth - 5) - vbucks.width), card.width, 495),
347 | price,
348 | blendColor,
349 | font=font,
350 | )
351 |
352 | card.paste(
353 | vbucks,
354 | ImageUtil.CenterX(self, (vbucks.width + (textWidth + 5)), card.width, 495),
355 | vbucks,
356 | )
357 |
358 | font = ImageUtil.Font(self, 56)
359 | textWidth, _ = font.getsize(name)
360 | change = 0
361 | if textWidth >= 270:
362 | # Ensure that the item name does not overflow
363 | font, textWidth, change = ImageUtil.FitTextX(self, name, 56, 260)
364 | canvas.text(
365 | ImageUtil.CenterX(self, textWidth, card.width, (425 + (change / 2))),
366 | name,
367 | (255, 255, 255),
368 | font=font,
369 | )
370 |
371 | return card
372 |
373 | def Tweet(self, date: str):
374 | """
375 | Tweet the current `itemshop.png` local file to Twitter using the credentials provided
376 | in `configuration.json`.
377 | """
378 |
379 | try:
380 | twitterAPI = twitter.Api(
381 | consumer_key=self.twitterAPIKey,
382 | consumer_secret=self.twitterAPISecret,
383 | access_token_key=self.twitterAccessToken,
384 | access_token_secret=self.twitterAccessSecret,
385 | )
386 |
387 | twitterAPI.VerifyCredentials()
388 | except Exception as e:
389 | log.critical(f"Failed to authenticate with Twitter, {e}")
390 |
391 | return
392 |
393 | body = f"#Fortnite Item Shop for {date}"
394 |
395 | if self.supportACreator is not None:
396 | body = f"{body}\n\nSupport-a-Creator Code: {self.supportACreator}"
397 |
398 | try:
399 | with open("itemshop.png", "rb") as shopImage:
400 | twitterAPI.PostUpdate(body, media=shopImage)
401 |
402 | log.info("Tweeted Item Shop")
403 | except Exception as e:
404 | log.critical(f"Failed to Tweet Item Shop, {e}")
405 |
406 |
407 | if __name__ == "__main__":
408 | try:
409 | Athena.main(Athena)
410 | except KeyboardInterrupt:
411 | log.info("Exiting...")
412 | exit()
413 |
--------------------------------------------------------------------------------
/util.py:
--------------------------------------------------------------------------------
1 | import json
2 | import locale
3 | import logging
4 | from datetime import datetime
5 |
6 | import requests
7 | from PIL import Image, ImageFont
8 |
9 | log = logging.getLogger(__name__)
10 |
11 |
12 | class Utility:
13 | """Class containing utilitarian functions intended to reduce duplicate code."""
14 |
15 | def GET(self, url: str, headers: dict, parameters: dict = {"language": "en"}):
16 | """
17 | Return the response of a successful HTTP GET request to the specified
18 | URL with the optionally provided header values.
19 | """
20 |
21 | res = requests.get(url, headers=headers, params=parameters)
22 |
23 | # HTTP 200 (OK)
24 | if res.status_code == 200:
25 | return res.text
26 | else:
27 | log.critical(f"Failed to GET {url} (HTTP {res.status_code})")
28 |
29 | def nowISO(self):
30 | """Return the current utc time in ISO8601 timestamp format."""
31 |
32 | return datetime.utcnow().isoformat()
33 |
34 | def ISOtoHuman(self, date: str, language: str):
35 | """Return the provided ISO8601 timestamp in human-readable format."""
36 |
37 | try:
38 | locale.setlocale(locale.LC_ALL, language)
39 | except locale.Error:
40 | log.warn(f"Unsupported locale configured, using system default")
41 |
42 | try:
43 | # Unix-supported zero padding removal
44 | return datetime.strptime(date, "%Y-%m-%d").strftime("%A, %B %-d, %Y")
45 | except ValueError:
46 | try:
47 | # Windows-supported zero padding removal
48 | return datetime.strptime(date, "%Y-%m-%d").strftime("%A, %B %#d, %Y")
49 | except Exception as e:
50 | log.error(self, f"Failed to convert to human-readable time, {e}")
51 |
52 | def ReadFile(self, filename: str, extension: str, directory: str = ""):
53 | """
54 | Read and return the contents of the specified file.
55 |
56 | Optionally specify a relative directory.
57 | """
58 |
59 | try:
60 | with open(
61 | f"{directory}{filename}.{extension}", "r", encoding="utf-8"
62 | ) as file:
63 | return file.read()
64 | except Exception as e:
65 | log.critical(f"Failed to read {filename}.{extension}, {e}")
66 |
67 |
68 | class ImageUtil:
69 | """Class containing utilitarian image-based functions intended to reduce duplicate code."""
70 |
71 | def Open(self, filename: str, directory: str = "assets/images/"):
72 | """Return the specified image file."""
73 |
74 | return Image.open(f"{directory}{filename}")
75 |
76 | def Download(self, url: str):
77 | """Download and return the raw file from the specified url as an image object."""
78 |
79 | res = requests.get(url, stream=True)
80 |
81 | # HTTP 200 (OK)
82 | if res.status_code == 200:
83 | return Image.open(res.raw)
84 | else:
85 | log.critical(f"Failed to GET {url} (HTTP {res.status_code})")
86 |
87 | def RatioResize(self, image: Image.Image, maxWidth: int, maxHeight: int):
88 | """Resize and return the provided image while maintaining aspect ratio."""
89 |
90 | ratio = max(maxWidth / image.width, maxHeight / image.height)
91 |
92 | return image.resize(
93 | (int(image.width * ratio), int(image.height * ratio)), Image.ANTIALIAS
94 | )
95 |
96 | def CenterX(self, foregroundWidth: int, backgroundWidth: int, distanceTop: int = 0):
97 | """Return the tuple necessary for horizontal centering and an optional vertical distance."""
98 |
99 | return (int(backgroundWidth / 2) - int(foregroundWidth / 2), distanceTop)
100 |
101 | def Font(
102 | self,
103 | size: int,
104 | font: str = "BurbankBigCondensed-Black.otf",
105 | directory: str = "assets/fonts/",
106 | ):
107 | """Return a font object with the specified font file and size."""
108 |
109 | try:
110 | return ImageFont.truetype(f"{directory}{font}", size)
111 | except OSError:
112 | log.warn(
113 | "BurbankBigCondensed-Black.otf not found, defaulted font to LuckiestGuy-Regular.ttf"
114 | )
115 |
116 | return ImageFont.truetype(f"{directory}LuckiestGuy-Regular.ttf", size)
117 | except Exception as e:
118 | log.error(f"Failed to load font, {e}")
119 |
120 | def FitTextX(
121 | self,
122 | text: str,
123 | size: int,
124 | maxSize: int,
125 | font: str = "BurbankBigCondensed-Black.otf",
126 | ):
127 | """Return the font and width which fits the provided text within the specified maxiumum width."""
128 |
129 | font = ImageUtil.Font(self, size)
130 | textWidth, _ = font.getsize(text)
131 | change = 0
132 |
133 | while textWidth >= maxSize:
134 | change += 1
135 | size -= 1
136 | font = ImageUtil.Font(self, size)
137 | textWidth, _ = font.getsize(text)
138 |
139 | return ImageUtil.Font(self, size), textWidth, change
140 |
--------------------------------------------------------------------------------