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.
- alphapil-0.1.0/PKG-INFO +115 -0
- alphapil-0.1.0/README.md +88 -0
- alphapil-0.1.0/pyproject.toml +53 -0
- alphapil-0.1.0/setup.cfg +4 -0
- alphapil-0.1.0/src/alphapil/__init__.py +24 -0
- alphapil-0.1.0/src/alphapil/engine.py +259 -0
- alphapil-0.1.0/src/alphapil/interpreter.py +342 -0
- alphapil-0.1.0/src/alphapil/modules/__init__.py +15 -0
- alphapil-0.1.0/src/alphapil/modules/base.py +278 -0
- alphapil-0.1.0/src/alphapil/modules/effects.py +170 -0
- alphapil-0.1.0/src/alphapil/modules/images.py +323 -0
- alphapil-0.1.0/src/alphapil/modules/masking.py +214 -0
- alphapil-0.1.0/src/alphapil/modules/shapes.py +250 -0
- alphapil-0.1.0/src/alphapil/modules/text.py +527 -0
- alphapil-0.1.0/src/alphapil/modules/utils.py +304 -0
- alphapil-0.1.0/src/alphapil.egg-info/PKG-INFO +115 -0
- alphapil-0.1.0/src/alphapil.egg-info/SOURCES.txt +18 -0
- alphapil-0.1.0/src/alphapil.egg-info/dependency_links.txt +1 -0
- alphapil-0.1.0/src/alphapil.egg-info/requires.txt +9 -0
- alphapil-0.1.0/src/alphapil.egg-info/top_level.txt +1 -0
alphapil-0.1.0/PKG-INFO
ADDED
|
@@ -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`
|
alphapil-0.1.0/README.md
ADDED
|
@@ -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
|
alphapil-0.1.0/setup.cfg
ADDED
|
@@ -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
|