alphapil 0.1.0__tar.gz

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.
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.4
2
+ Name: alphapil
3
+ Version: 0.1.0
4
+ Summary: An asynchronous, template-based image generation engine
5
+ Author-email: AlphaPIL Team <team@alphapil.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/MeraLabs/AlphaPIL
8
+ Project-URL: Repository, https://github.com/MeraLabs/AlphaPIL
9
+ Project-URL: Documentation, https://alphapil.readthedocs.io
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: Pillow>=9.0.0
20
+ Requires-Dist: aiohttp>=3.8.0
21
+ Requires-Dist: discord.py>=2.0.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
24
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
25
+ Requires-Dist: black>=22.0.0; extra == "dev"
26
+ Requires-Dist: isort>=5.10.0; extra == "dev"
27
+
28
+ # AlphaPIL
29
+
30
+ ### About
31
+ **AlphaPIL** is an asynchronous, template-based image generation engine built on top of Pillow (PIL). It is designed to make it easy to build dynamic images using a simple, readable template language.
32
+
33
+ It is open-source and free to use, providing a powerful recursive parser for handling nested functions in image generation templates, with full support for asynchronous operations.
34
+
35
+ AlphaPIL is suitable for developers building Discord bots, automated social media graphics, or any application requiring high-performance, template-driven image creation. This package is proudly maintained by the **[MeraLabs](https://github.com/MeraLabs)** organization.
36
+
37
+ ### Features
38
+ - **60+ Pre-built Functions**: AlphaPIL comes packed with over 60 pre-built functions for shapes, text manipulation, image filtering, and masking.
39
+ - **Asynchronous by Design**: Built from the ground up to support `asyncio`, making it perfect for modern web frameworks and Discord bots.
40
+ - **Powerful DSL**: Use a simple `$function[arg1;arg2]` syntax to define your images. Supports nested functions like `$drawText[$toUpper[hello];10;10]`.
41
+ - **Advanced Text Rendering**: Support for text gradients, strokes, automatic wrapping, and auto-sizing.
42
+ - **Layer & Masking System**: Create complex compositions with multi-layer support and alpha masking.
43
+ - **Image Caching**: Built-in caching for remote images to ensure fast rendering during repeated operations.
44
+
45
+ ### Setup
46
+ Install AlphaPIL via pip:
47
+ ```bash
48
+ pip install alphapil
49
+ ```
50
+
51
+ Basic usage in your Python project:
52
+ ```python
53
+ import asyncio
54
+ from alphapil import CanvasEngine
55
+
56
+ async def main():
57
+ engine = CanvasEngine()
58
+
59
+ template = """
60
+ $createCanvas[800;400;#1a1a1a]
61
+ $setColor[#ffffff]
62
+ $setFont[Arial;40]
63
+ $drawText[Hello from AlphaPIL!;50;50]
64
+ $save[output.png]
65
+ """
66
+
67
+ await engine.render_template(template)
68
+ print("Image generated successfully!")
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ### Rendering with Data
74
+ You can inject dynamic data into your templates easily:
75
+
76
+ ```python
77
+ data = {
78
+ "username": "Alex",
79
+ "score": "950"
80
+ }
81
+
82
+ template = """
83
+ $createCanvas[500;200;white]
84
+ $setColor[black]
85
+ $setFont[Roboto;30]
86
+ $drawText[User: {username};20;20]
87
+ $drawText[Score: {score};20;60]
88
+ """
89
+
90
+ image_bytes = await engine.render_template(template, data=data)
91
+ ```
92
+
93
+ ### Advanced Masking
94
+ AlphaPIL supports complex masking for creating avatars or stylized images:
95
+
96
+ ```python
97
+ template = """
98
+ $createCanvas[400;400;transparent]
99
+ $createLayer[mask]
100
+ $drawCircle[200;200;180;white]
101
+ $switchLayer[main]
102
+ $drawImage[https://example.com/avatar.png;0;0;400;400]
103
+ $applyMask[mask]
104
+ """
105
+ ```
106
+
107
+ ### Support
108
+ Need help? Join our official Discord community for support and updates!
109
+ **[Join MeraLabs Discord](https://discord.gg/AXbuM6aaeQ)**
110
+
111
+ ### Contributing
112
+ Refer to the [Contribution Documentation](https://github.com/MeraLabs/AlphaPIL/blob/main/CONTRIBUTING.md) for more information.
113
+
114
+ ### Keywords
115
+ `alphapil` `image-generation` `pillow` `python` `async` `discord-bot` `template-engine` `meralabs` `canvas` `dsl`
@@ -0,0 +1,88 @@
1
+ # AlphaPIL
2
+
3
+ ### About
4
+ **AlphaPIL** is an asynchronous, template-based image generation engine built on top of Pillow (PIL). It is designed to make it easy to build dynamic images using a simple, readable template language.
5
+
6
+ It is open-source and free to use, providing a powerful recursive parser for handling nested functions in image generation templates, with full support for asynchronous operations.
7
+
8
+ AlphaPIL is suitable for developers building Discord bots, automated social media graphics, or any application requiring high-performance, template-driven image creation. This package is proudly maintained by the **[MeraLabs](https://github.com/MeraLabs)** organization.
9
+
10
+ ### Features
11
+ - **60+ Pre-built Functions**: AlphaPIL comes packed with over 60 pre-built functions for shapes, text manipulation, image filtering, and masking.
12
+ - **Asynchronous by Design**: Built from the ground up to support `asyncio`, making it perfect for modern web frameworks and Discord bots.
13
+ - **Powerful DSL**: Use a simple `$function[arg1;arg2]` syntax to define your images. Supports nested functions like `$drawText[$toUpper[hello];10;10]`.
14
+ - **Advanced Text Rendering**: Support for text gradients, strokes, automatic wrapping, and auto-sizing.
15
+ - **Layer & Masking System**: Create complex compositions with multi-layer support and alpha masking.
16
+ - **Image Caching**: Built-in caching for remote images to ensure fast rendering during repeated operations.
17
+
18
+ ### Setup
19
+ Install AlphaPIL via pip:
20
+ ```bash
21
+ pip install alphapil
22
+ ```
23
+
24
+ Basic usage in your Python project:
25
+ ```python
26
+ import asyncio
27
+ from alphapil import CanvasEngine
28
+
29
+ async def main():
30
+ engine = CanvasEngine()
31
+
32
+ template = """
33
+ $createCanvas[800;400;#1a1a1a]
34
+ $setColor[#ffffff]
35
+ $setFont[Arial;40]
36
+ $drawText[Hello from AlphaPIL!;50;50]
37
+ $save[output.png]
38
+ """
39
+
40
+ await engine.render_template(template)
41
+ print("Image generated successfully!")
42
+
43
+ asyncio.run(main())
44
+ ```
45
+
46
+ ### Rendering with Data
47
+ You can inject dynamic data into your templates easily:
48
+
49
+ ```python
50
+ data = {
51
+ "username": "Alex",
52
+ "score": "950"
53
+ }
54
+
55
+ template = """
56
+ $createCanvas[500;200;white]
57
+ $setColor[black]
58
+ $setFont[Roboto;30]
59
+ $drawText[User: {username};20;20]
60
+ $drawText[Score: {score};20;60]
61
+ """
62
+
63
+ image_bytes = await engine.render_template(template, data=data)
64
+ ```
65
+
66
+ ### Advanced Masking
67
+ AlphaPIL supports complex masking for creating avatars or stylized images:
68
+
69
+ ```python
70
+ template = """
71
+ $createCanvas[400;400;transparent]
72
+ $createLayer[mask]
73
+ $drawCircle[200;200;180;white]
74
+ $switchLayer[main]
75
+ $drawImage[https://example.com/avatar.png;0;0;400;400]
76
+ $applyMask[mask]
77
+ """
78
+ ```
79
+
80
+ ### Support
81
+ Need help? Join our official Discord community for support and updates!
82
+ **[Join MeraLabs Discord](https://discord.gg/AXbuM6aaeQ)**
83
+
84
+ ### Contributing
85
+ Refer to the [Contribution Documentation](https://github.com/MeraLabs/AlphaPIL/blob/main/CONTRIBUTING.md) for more information.
86
+
87
+ ### Keywords
88
+ `alphapil` `image-generation` `pillow` `python` `async` `discord-bot` `template-engine` `meralabs` `canvas` `dsl`
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "alphapil"
7
+ version = "0.1.0"
8
+ description = "An asynchronous, template-based image generation engine"
9
+ authors = [{name = "AlphaPIL Team", email = "team@alphapil.dev"}]
10
+ license = "MIT"
11
+ readme = "README.md"
12
+ requires-python = ">=3.8"
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.8",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ ]
22
+ dependencies = [
23
+ "Pillow>=9.0.0",
24
+ "aiohttp>=3.8.0",
25
+ "discord.py>=2.0.0",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ dev = [
30
+ "pytest>=7.0.0",
31
+ "pytest-asyncio>=0.21.0",
32
+ "black>=22.0.0",
33
+ "isort>=5.10.0",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/MeraLabs/AlphaPIL"
38
+ Repository = "https://github.com/MeraLabs/AlphaPIL"
39
+ Documentation = "https://alphapil.readthedocs.io"
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["src"]
43
+
44
+ [tool.setuptools.package-dir]
45
+ "" = "src"
46
+
47
+ [tool.black]
48
+ line-length = 88
49
+ target-version = ['py38']
50
+
51
+ [tool.isort]
52
+ profile = "black"
53
+ line_length = 88
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,24 @@
1
+ """
2
+ AlphaPIL - An asynchronous, template-based image generation engine.
3
+
4
+ This package provides a powerful recursive parser for handling nested functions
5
+ in image generation templates, with support for asynchronous operations.
6
+ """
7
+
8
+ __version__ = "0.1.0"
9
+ __author__ = "AlphaPIL Team"
10
+
11
+ from .engine import CanvasEngine
12
+ from .interpreter import CanvasInterpreter
13
+ from .modules import AlphaMixin, ShapesMixin, TextMixin, ImagesMixin, UtilsMixin, MaskingMixin
14
+
15
+ __all__ = [
16
+ "CanvasEngine",
17
+ "CanvasInterpreter",
18
+ "AlphaMixin",
19
+ "ShapesMixin",
20
+ "TextMixin",
21
+ "ImagesMixin",
22
+ "UtilsMixin",
23
+ "MaskingMixin"
24
+ ]
@@ -0,0 +1,259 @@
1
+ """
2
+ CanvasEngine - High-level canvas operations for AlphaPIL.
3
+
4
+ This module provides the CanvasEngine class that inherits from CanvasInterpreter
5
+ and all module mixins to provide comprehensive image generation capabilities.
6
+ """
7
+
8
+ import io
9
+ from typing import Tuple, Union, Optional
10
+ from PIL import Image, ImageDraw, ImageFont
11
+ from .interpreter import CanvasInterpreter
12
+ from .modules import AlphaMixin, ShapesMixin, TextMixin, ImagesMixin, UtilsMixin, MaskingMixin
13
+
14
+
15
+ class CanvasEngine(CanvasInterpreter, AlphaMixin, ShapesMixin, TextMixin, ImagesMixin, UtilsMixin, MaskingMixin):
16
+ """
17
+ High-level canvas engine that extends CanvasInterpreter with Pillow-based
18
+ image generation capabilities and all module mixins.
19
+ """
20
+
21
+ def __init__(self):
22
+ """Initialize the canvas engine and register built-in functions."""
23
+ # Initialize all parent classes properly
24
+ CanvasInterpreter.__init__(self)
25
+ AlphaMixin.__init__(self)
26
+ ShapesMixin.__init__(self)
27
+ TextMixin.__init__(self)
28
+ ImagesMixin.__init__(self)
29
+ UtilsMixin.__init__(self)
30
+ MaskingMixin.__init__(self)
31
+
32
+ # Initialize mixin states
33
+ self._init_state()
34
+ self._init_text()
35
+
36
+ # Initialize canvas-specific attributes
37
+ self.canvas: Optional[Image.Image] = None
38
+ self.draw: Optional[ImageDraw.Draw] = None
39
+ self.canvas_size: Tuple[int, int] = (0, 0)
40
+
41
+ # Register all built-in functions from modules
42
+ self._register_builtin_functions()
43
+
44
+ async def render_template(self, template_text: str, data: dict = None) -> bytes:
45
+ """
46
+ Render a template with optional data injection (Async).
47
+
48
+ Args:
49
+ template_text: Template content as string
50
+ data: Optional dictionary of variables to set
51
+
52
+ Returns:
53
+ Canvas image as bytes
54
+
55
+ Raises:
56
+ RuntimeError: If template rendering fails
57
+ """
58
+ try:
59
+ # Reset engine state
60
+ self.reset()
61
+
62
+ # Inject data if provided
63
+ if data:
64
+ for key, value in data.items():
65
+ self.set_variable(key, str(value))
66
+
67
+ # Parse template line by line
68
+ lines = template_text.strip().split('\n')
69
+ for line_num, line in enumerate(lines, 1):
70
+ line = line.strip()
71
+
72
+ # Skip comments and empty lines
73
+ if not line or line.startswith('#'):
74
+ continue
75
+
76
+ # Parse and execute the line (awaiting async parse)
77
+ result = await self.parse(line)
78
+
79
+ # Return canvas as bytes
80
+ return self.get_canvas_bytes()
81
+
82
+ except Exception as e:
83
+ raise RuntimeError(f"Template rendering failed: {e}")
84
+
85
+ async def render_template_file(self, template_path: str, data: dict = None) -> bytes:
86
+ """
87
+ Render a template from file with optional data injection (Async).
88
+
89
+ Args:
90
+ template_path: Path to template file
91
+ data: Optional dictionary of variables to set
92
+
93
+ Returns:
94
+ Canvas image as bytes
95
+
96
+ Raises:
97
+ FileNotFoundError: If template file doesn't exist
98
+ RuntimeError: If template rendering fails
99
+ """
100
+ try:
101
+ # Use async file reading if possible, but standard open is fine for text files
102
+ # or could use aiofiles if dependency added. For now sync read is okay.
103
+ with open(template_path, 'r', encoding='utf-8') as f:
104
+ template_text = f.read()
105
+ return await self.render_template(template_text, data)
106
+ except FileNotFoundError:
107
+ raise FileNotFoundError(f"Template file not found: {template_path}")
108
+ except Exception as e:
109
+ raise RuntimeError(f"Template rendering failed: {e}")
110
+
111
+ def _register_builtin_functions(self) -> None:
112
+ """Register all built-in canvas manipulation functions from all modules."""
113
+ # Core canvas functions
114
+ self.register_function("createCanvas", self._create_canvas)
115
+ self.register_function("save", self._save_canvas)
116
+ self.register_function("setVar", self._set_var)
117
+
118
+ # State management functions
119
+ # State management functions - Registered at end to use new implementations
120
+ # self.register_function("setFont", self._set_font)
121
+ # self.register_function("setColor", self._set_color)
122
+ # self.register_function("setStroke", self._set_stroke)
123
+
124
+ # Shape functions from ShapesMixin
125
+ self.register_function("drawRect", self._draw_rect)
126
+ self.register_function("drawCircle", self._draw_circle)
127
+ self.register_function("drawRoundedRect", self._draw_rounded_rect)
128
+ self.register_function("drawLine", self._draw_line)
129
+
130
+ # Text functions from TextMixin
131
+ self.register_function("drawText", self._draw_text)
132
+ self.register_function("drawTextStroke", self._draw_text_stroke)
133
+ self.register_function("drawTextGradient", self._draw_text_gradient)
134
+ self.register_function("toUpper", self._to_upper)
135
+ self.register_function("toLower", self._to_lower)
136
+ self.register_function("toTitle", self._to_title)
137
+ self.register_function("measureText", self._measure_text)
138
+ self.register_function("wrapText", self._wrap_text)
139
+ self.register_function("autoSizeText", self._auto_size_text)
140
+ self.register_function("truncateText", self._truncate_text)
141
+ self.register_function("drawTextMid", self._draw_text_mid)
142
+ self.register_function("drawTextIn", self._draw_text_in)
143
+
144
+ # Image functions from ImagesMixin
145
+ self.register_function("drawImage", self._draw_image)
146
+ self.register_function("useImageAsCanvas", self._use_image_as_canvas)
147
+ self.register_function("imageFilter", self._image_filter)
148
+ self.register_function("clearImageCache", self.clear_image_cache)
149
+
150
+ # Utility functions from UtilsMixin
151
+ self.register_function("math", self._math)
152
+ self.register_function("if", self._if)
153
+ self.register_function("random", self._random)
154
+ self.register_function("getHex", self._get_hex)
155
+ self.register_function("replace", self._replace)
156
+ self.register_function("length", self._length)
157
+ self.register_function("substring", self._substring)
158
+ self.register_function("join", self._join)
159
+ self.register_function("split", self._split)
160
+
161
+ # Masking functions from MaskingMixin
162
+ self.register_function("createLayer", self._create_layer)
163
+ self.register_function("switchLayer", self._switch_layer)
164
+ self.register_function("mergeLayer", self._merge_layer)
165
+ self.register_function("applyMask", self._apply_mask)
166
+
167
+ # State management commands
168
+ self.register_function("setFont", self._cmd_set_font)
169
+ self.register_function("loadFont", self._load_font)
170
+ self.register_function("setColor", self._cmd_set_color)
171
+ self.register_function("setStroke", self._cmd_set_stroke)
172
+
173
+ def _create_canvas(self, width: str, height: str, color: str = "white") -> str:
174
+ """
175
+ Create a new canvas with specified dimensions and background color.
176
+
177
+ Args:
178
+ width: Canvas width as string
179
+ height: Canvas height as string
180
+ color: Background color (default: "white")
181
+
182
+ Returns:
183
+ Confirmation message
184
+ """
185
+ try:
186
+ w = int(width)
187
+ h = int(height)
188
+ self.canvas_size = (w, h)
189
+
190
+ # Parse color using the helper from AlphaMixin
191
+ bg_color = self._get_color(color) or (255, 255, 255)
192
+
193
+ self.canvas = Image.new("RGB", (w, h), bg_color)
194
+ self.draw = ImageDraw.Draw(self.canvas)
195
+ return f"Canvas created: {w}x{h}"
196
+ except ValueError as e:
197
+ raise ValueError(f"Invalid canvas dimensions: {e}")
198
+
199
+ def _set_var(self, name: str, value: str) -> str:
200
+ """
201
+ Set a variable value.
202
+
203
+ Args:
204
+ name: Variable name (without {} wrapper)
205
+ value: Variable value
206
+
207
+ Returns:
208
+ Confirmation message
209
+ """
210
+ self.set_variable(name, value)
211
+ return f"Variable {name} set to {value}"
212
+
213
+ def _save_canvas(self, filename: str = "output.png") -> str:
214
+ """
215
+ Save the current canvas to a file with maximum quality.
216
+ """
217
+ if not self.canvas:
218
+ raise RuntimeError("No canvas to save. Call $createCanvas first.")
219
+
220
+ try:
221
+ # Set high quality parameters for various formats
222
+ save_params = {"optimize": True}
223
+ if filename.lower().endswith(('.jpg', '.jpeg')):
224
+ save_params.update({"quality": 100, "subsampling": 0})
225
+
226
+ self.canvas.save(filename, **save_params)
227
+ return f"Canvas saved as {filename}"
228
+ except Exception as e:
229
+ raise RuntimeError(f"Failed to save canvas: {e}")
230
+
231
+ def get_canvas_bytes(self, format: str = "PNG") -> bytes:
232
+ """
233
+ Get the canvas as bytes with maximum quality.
234
+ """
235
+ if not self.canvas:
236
+ raise RuntimeError("No canvas available. Call $createCanvas first.")
237
+
238
+ img_bytes = io.BytesIO()
239
+
240
+ # Set high quality parameters
241
+ save_params = {"format": format, "optimize": True}
242
+ if format.upper() in ["JPEG", "JPG"]:
243
+ save_params.update({"quality": 100, "subsampling": 0})
244
+
245
+ self.canvas.save(img_bytes, **save_params)
246
+ img_bytes.seek(0)
247
+ return img_bytes.getvalue()
248
+
249
+ def reset(self) -> None:
250
+ """Reset the canvas, drawing context, and state."""
251
+ self.canvas = None
252
+ self.draw = None
253
+ self.canvas_size = (0, 0)
254
+ self._init_state()
255
+ self._init_text()
256
+ if hasattr(self, '_image_cache'):
257
+ self._image_cache.clear()
258
+
259
+ # State management is now handled via AlphaMixin _cmd_* methods