imgzip 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.
imgzip-0.1.0/LICENSE ADDED
File without changes
imgzip-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: imgzip
3
+ Version: 0.1.0
4
+ Summary: A lightweight Python library for compressing images with ease
5
+ Home-page: https://github.com/RKSAHOO4414/imgzip
6
+ Author: RKSAHOO4414
7
+ Author-email: your_email@example.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.8
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: Pillow>=9.0.0
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: license-file
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # imgzip
30
+
31
+ A lightweight Python library for compressing images with ease.
32
+
33
+ ## Features
34
+
35
+ - ✅ Simple and intuitive API
36
+ - ✅ Support for JPG, PNG, BMP, GIF formats
37
+ - ✅ Batch compression for multiple images
38
+ - ✅ Customizable compression quality
39
+ - ✅ Optional image resizing
40
+ - ✅ Automatic format conversion
41
+ - ✅ Detailed compression statistics
42
+
43
+ ## Installation
44
+
45
+ Install `imgzip` from PyPI:
46
+
47
+ ```bash
48
+ pip install imgzip
imgzip-0.1.0/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # imgzip
2
+
3
+ A lightweight Python library for compressing images with ease.
4
+
5
+ ## Features
6
+
7
+ - ✅ Simple and intuitive API
8
+ - ✅ Support for JPG, PNG, BMP, GIF formats
9
+ - ✅ Batch compression for multiple images
10
+ - ✅ Customizable compression quality
11
+ - ✅ Optional image resizing
12
+ - ✅ Automatic format conversion
13
+ - ✅ Detailed compression statistics
14
+
15
+ ## Installation
16
+
17
+ Install `imgzip` from PyPI:
18
+
19
+ ```bash
20
+ pip install imgzip
@@ -0,0 +1,8 @@
1
+ """
2
+ imgzip - A lightweight Python library for compressing images with ease
3
+ """
4
+
5
+ from .compressor import ImageCompressor
6
+
7
+ __version__ = "0.1.0"
8
+ __all__ = ["ImageCompressor"]
@@ -0,0 +1,137 @@
1
+ """
2
+ Core image compression module
3
+ """
4
+
5
+ import os
6
+ from PIL import Image
7
+ from pathlib import Path
8
+
9
+
10
+ class ImageCompressor:
11
+ """
12
+ A simple and efficient image compressor for common image formats.
13
+
14
+ Supported formats: JPG, PNG, BMP, GIF
15
+ """
16
+
17
+ def __init__(self, quality=85, max_width=None, max_height=None):
18
+ """
19
+ Initialize the ImageCompressor.
20
+
21
+ Args:
22
+ quality (int): JPEG quality level (1-100). Default is 85.
23
+ Higher = better quality, larger file size.
24
+ max_width (int): Maximum width in pixels. Maintains aspect ratio if set.
25
+ max_height (int): Maximum height in pixels. Maintains aspect ratio if set.
26
+ """
27
+ if not 1 <= quality <= 100:
28
+ raise ValueError("Quality must be between 1 and 100")
29
+
30
+ self.quality = quality
31
+ self.max_width = max_width
32
+ self.max_height = max_height
33
+
34
+ def compress(self, input_path, output_path=None, format=None):
35
+ """
36
+ Compress a single image file.
37
+
38
+ Args:
39
+ input_path (str): Path to the input image file.
40
+ output_path (str): Path to save the compressed image.
41
+ If None, saves as 'compressed_<original_name>'.
42
+ format (str): Image format for output (JPG, PNG). If None, uses input format.
43
+
44
+ Returns:
45
+ dict: Dictionary with compression details (original size, compressed size, ratio).
46
+
47
+ Raises:
48
+ FileNotFoundError: If input file doesn't exist.
49
+ ValueError: If file format is not supported.
50
+ """
51
+ # Validate input file
52
+ if not os.path.exists(input_path):
53
+ raise FileNotFoundError(f"Input file not found: {input_path}")
54
+
55
+ # Open image
56
+ try:
57
+ img = Image.open(input_path)
58
+ except Exception as e:
59
+ raise ValueError(f"Cannot open image file: {e}")
60
+
61
+ # Get original file size
62
+ original_size = os.path.getsize(input_path)
63
+
64
+ # Determine output path
65
+ if output_path is None:
66
+ input_name = Path(input_path).stem
67
+ input_ext = Path(input_path).suffix
68
+ output_path = f"compressed_{input_name}{input_ext}"
69
+
70
+ # Determine output format
71
+ if format is None:
72
+ format = img.format or "JPEG"
73
+ format = format.upper()
74
+
75
+ # Resize if max dimensions are set
76
+ if self.max_width or self.max_height:
77
+ img.thumbnail((self.max_width or img.width, self.max_height or img.height), Image.Resampling.LANCZOS)
78
+
79
+ # Convert RGBA to RGB if saving as JPEG
80
+ if format == "JPEG" and img.mode in ("RGBA", "LA", "P"):
81
+ rgb_img = Image.new("RGB", img.size, (255, 255, 255))
82
+ rgb_img.paste(img, mask=img.split()[-1] if img.mode == "RGBA" else None)
83
+ img = rgb_img
84
+
85
+ # Save compressed image
86
+ img.save(output_path, format=format, quality=self.quality, optimize=True)
87
+
88
+ # Get compressed file size
89
+ compressed_size = os.path.getsize(output_path)
90
+ compression_ratio = (1 - (compressed_size / original_size)) * 100
91
+
92
+ return {
93
+ "input_path": input_path,
94
+ "output_path": output_path,
95
+ "original_size_kb": round(original_size / 1024, 2),
96
+ "compressed_size_kb": round(compressed_size / 1024, 2),
97
+ "compression_ratio_percent": round(compression_ratio, 2),
98
+ }
99
+
100
+ def compress_batch(self, input_folder, output_folder=None):
101
+ """
102
+ Compress all images in a folder.
103
+
104
+ Args:
105
+ input_folder (str): Path to folder containing images.
106
+ output_folder (str): Path to save compressed images.
107
+ If None, creates 'compressed' folder in input folder.
108
+
109
+ Returns:
110
+ list: List of dictionaries with compression details for each image.
111
+ """
112
+ if not os.path.isdir(input_folder):
113
+ raise FileNotFoundError(f"Input folder not found: {input_folder}")
114
+
115
+ if output_folder is None:
116
+ output_folder = os.path.join(input_folder, "compressed")
117
+
118
+ os.makedirs(output_folder, exist_ok=True)
119
+
120
+ results = []
121
+ supported_formats = {".jpg", ".jpeg", ".png", ".bmp", ".gif"}
122
+
123
+ for filename in os.listdir(input_folder):
124
+ if Path(filename).suffix.lower() in supported_formats:
125
+ input_path = os.path.join(input_folder, filename)
126
+ output_path = os.path.join(output_folder, f"compressed_{filename}")
127
+
128
+ try:
129
+ result = self.compress(input_path, output_path)
130
+ results.append(result)
131
+ except Exception as e:
132
+ results.append({
133
+ "input_path": input_path,
134
+ "error": str(e)
135
+ })
136
+
137
+ return results
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: imgzip
3
+ Version: 0.1.0
4
+ Summary: A lightweight Python library for compressing images with ease
5
+ Home-page: https://github.com/RKSAHOO4414/imgzip
6
+ Author: RKSAHOO4414
7
+ Author-email: your_email@example.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.8
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: Pillow>=9.0.0
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: license-file
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # imgzip
30
+
31
+ A lightweight Python library for compressing images with ease.
32
+
33
+ ## Features
34
+
35
+ - ✅ Simple and intuitive API
36
+ - ✅ Support for JPG, PNG, BMP, GIF formats
37
+ - ✅ Batch compression for multiple images
38
+ - ✅ Customizable compression quality
39
+ - ✅ Optional image resizing
40
+ - ✅ Automatic format conversion
41
+ - ✅ Detailed compression statistics
42
+
43
+ ## Installation
44
+
45
+ Install `imgzip` from PyPI:
46
+
47
+ ```bash
48
+ pip install imgzip
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ imgzip/__init__.py
5
+ imgzip/compressor.py
6
+ imgzip.egg-info/PKG-INFO
7
+ imgzip.egg-info/SOURCES.txt
8
+ imgzip.egg-info/dependency_links.txt
9
+ imgzip.egg-info/requires.txt
10
+ imgzip.egg-info/top_level.txt
11
+ tests/__init__.py
12
+ tests/test_compressor.py
@@ -0,0 +1 @@
1
+ Pillow>=9.0.0
@@ -0,0 +1,2 @@
1
+ imgzip
2
+ tests
imgzip-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
imgzip-0.1.0/setup.py ADDED
@@ -0,0 +1,28 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="imgzip",
8
+ version="0.1.0",
9
+ author="RKSAHOO4414",
10
+ author_email="your_email@example.com",
11
+ description="A lightweight Python library for compressing images with ease",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/RKSAHOO4414/imgzip",
15
+ packages=find_packages(),
16
+ classifiers=[
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Operating System :: OS Independent",
23
+ ],
24
+ python_requires=">=3.8",
25
+ install_requires=[
26
+ "Pillow>=9.0.0",
27
+ ],
28
+ )
@@ -0,0 +1 @@
1
+ # Tests package
@@ -0,0 +1,100 @@
1
+ """
2
+ Unit tests for imgzip compressor
3
+ """
4
+
5
+ import unittest
6
+ import os
7
+ import tempfile
8
+ from pathlib import Path
9
+ from PIL import Image
10
+ from imgzip.compressor import ImageCompressor
11
+
12
+
13
+ class TestImageCompressor(unittest.TestCase):
14
+ """Test cases for ImageCompressor class"""
15
+
16
+ def setUp(self):
17
+ """Set up test fixtures"""
18
+ self.temp_dir = tempfile.mkdtemp()
19
+ self.compressor = ImageCompressor(quality=85)
20
+
21
+ # Create a test image
22
+ self.test_image_path = os.path.join(self.temp_dir, "test_image.png")
23
+ test_image = Image.new("RGB", (1000, 1000), color="red")
24
+ test_image.save(self.test_image_path)
25
+
26
+ def tearDown(self):
27
+ """Clean up test files"""
28
+ for file in os.listdir(self.temp_dir):
29
+ file_path = os.path.join(self.temp_dir, file)
30
+ if os.path.isfile(file_path):
31
+ os.remove(file_path)
32
+ os.rmdir(self.temp_dir)
33
+
34
+ def test_quality_validation(self):
35
+ """Test that quality parameter is validated"""
36
+ with self.assertRaises(ValueError):
37
+ ImageCompressor(quality=0)
38
+
39
+ with self.assertRaises(ValueError):
40
+ ImageCompressor(quality=101)
41
+
42
+ def test_compress_file_exists(self):
43
+ """Test compression of an existing file"""
44
+ output_path = os.path.join(self.temp_dir, "compressed_test.png")
45
+ result = self.compressor.compress(self.test_image_path, output_path)
46
+
47
+ self.assertTrue(os.path.exists(output_path))
48
+ self.assertIn("compression_ratio_percent", result)
49
+ self.assertGreater(result["compression_ratio_percent"], 0)
50
+
51
+ def test_compress_nonexistent_file(self):
52
+ """Test that compression fails for nonexistent file"""
53
+ with self.assertRaises(FileNotFoundError):
54
+ self.compressor.compress("/nonexistent/path/image.png")
55
+
56
+ def test_auto_output_path(self):
57
+ """Test automatic output path generation"""
58
+ original_dir = os.getcwd()
59
+ try:
60
+ os.chdir(self.temp_dir)
61
+ result = self.compressor.compress(self.test_image_path)
62
+
63
+ self.assertTrue(os.path.exists(result["output_path"]))
64
+ self.assertIn("compressed_", result["output_path"])
65
+ finally:
66
+ os.chdir(original_dir)
67
+
68
+ def test_resize_functionality(self):
69
+ """Test image resizing during compression"""
70
+ compressor_with_resize = ImageCompressor(quality=85, max_width=500, max_height=500)
71
+ output_path = os.path.join(self.temp_dir, "resized.png")
72
+
73
+ compressor_with_resize.compress(self.test_image_path, output_path)
74
+
75
+ # Verify resized image
76
+ compressed_img = Image.open(output_path)
77
+ self.assertLessEqual(compressed_img.width, 500)
78
+ self.assertLessEqual(compressed_img.height, 500)
79
+
80
+ def test_compression_ratio(self):
81
+ """Test that compression actually reduces file size"""
82
+ # Create a more complex image that compresses better
83
+ import random
84
+ test_image = Image.new("RGB", (1000, 1000))
85
+ pixels = test_image.load()
86
+ for i in range(1000):
87
+ for j in range(1000):
88
+ pixels[i, j] = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
89
+
90
+ complex_image_path = os.path.join(self.temp_dir, "complex_image.png")
91
+ test_image.save(complex_image_path)
92
+
93
+ output_path = os.path.join(self.temp_dir, "compressed_test.jpg")
94
+ result = self.compressor.compress(complex_image_path, output_path, format="JPEG")
95
+
96
+ self.assertLess(result["compressed_size_kb"], result["original_size_kb"])
97
+
98
+
99
+ if __name__ == "__main__":
100
+ unittest.main()