pillow-minecraft-map 1.0.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,3 @@
1
+ /.idea
2
+ /.venv
3
+ /src/pillow_minecraft_map/__pycache__
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christopher Bruns
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.
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: pillow-minecraft-map
3
+ Version: 1.0.0
4
+ Summary: A Pillow Image plugin to open Minecraft .dat map files.
5
+ Project-URL: Homepage, https://github.com/cmbruns/pillow-minecraft-map
6
+ Author-email: Christopher Bruns <cmbruns@rotatingpenguin.com>
7
+ License-File: LICENSE
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Multimedia :: Graphics
12
+ Requires-Dist: pillow>=10.0.0
13
+ Description-Content-Type: text/markdown
14
+
15
+ # pillow-minecraft-map
16
+
17
+ A plugin for [Pillow (PIL)](https://pillow.readthedocs.io) that enables loading, viewing, and manipulation of Minecraft `.dat` map item files.
18
+
19
+ ## Features
20
+
21
+ - Open Minecraft maps using standard Pillow syntax (`Image.open("map_0.dat")`).
22
+ - Exposes location details (`xCenter`, `zCenter`, `scale`) to Pillow's `.info` attribute.
23
+
24
+ ## Important Note on Compatibility
25
+
26
+ > **Only Minecraft Java Edition map files are supported at this time.**
27
+ > Minecraft Bedrock Edition utilizes a different map info format.
28
+
29
+ ## Installation
30
+
31
+ Install the package via pip:
32
+
33
+ ```bash
34
+ pip install pillow-minecraft-map
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Import the module to register the `.dat` plugin handlers into Pillow's image loader database.
40
+
41
+ ### Reading Map Images
42
+
43
+ ```python
44
+ from PIL import Image
45
+ import pillow_minecraft_map # Activates the extension
46
+
47
+ # Open the map file
48
+ with Image.open("tests/data/map_130.dat") as img:
49
+ # Convert from 8-bit color-indexed format ("P") to True Color ("RGB")
50
+ rgb_img = img.convert("RGB")
51
+
52
+ # Open inside your system's default image viewer
53
+ rgb_img.show()
54
+
55
+ # Export it to a standard format
56
+ rgb_img.save("minecraft_map_render.png")
57
+ ```
58
+
59
+ ### Accessing Map Metadata (`info`)
60
+
61
+ The plugin maps in-game positioning values into the image's `.info` dictionary:
62
+
63
+ ```python
64
+ from PIL import Image
65
+ import pillow_minecraft_map
66
+
67
+ with Image.open("tests/data/map_130.dat") as img:
68
+ x = img.info.get("x_center")
69
+ z = img.info.get("z_center")
70
+ scale_factor = img.info.get("scale")
71
+ version = img.info.get("data_version")
72
+
73
+ # Calculate real-world block coverage
74
+ block_width = 128 * (2 ** scale_factor)
75
+
76
+ print(f"Minecraft DataVersion: {version}")
77
+ print(f"Center Coordinates: X={x}, Z={z}")
78
+ print(f"Scale: 1:{2**scale_factor} ({block_width}x{block_width} blocks total)")
79
+ ```
80
+
81
+ ## License
82
+
83
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,69 @@
1
+ # pillow-minecraft-map
2
+
3
+ A plugin for [Pillow (PIL)](https://pillow.readthedocs.io) that enables loading, viewing, and manipulation of Minecraft `.dat` map item files.
4
+
5
+ ## Features
6
+
7
+ - Open Minecraft maps using standard Pillow syntax (`Image.open("map_0.dat")`).
8
+ - Exposes location details (`xCenter`, `zCenter`, `scale`) to Pillow's `.info` attribute.
9
+
10
+ ## Important Note on Compatibility
11
+
12
+ > **Only Minecraft Java Edition map files are supported at this time.**
13
+ > Minecraft Bedrock Edition utilizes a different map info format.
14
+
15
+ ## Installation
16
+
17
+ Install the package via pip:
18
+
19
+ ```bash
20
+ pip install pillow-minecraft-map
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Import the module to register the `.dat` plugin handlers into Pillow's image loader database.
26
+
27
+ ### Reading Map Images
28
+
29
+ ```python
30
+ from PIL import Image
31
+ import pillow_minecraft_map # Activates the extension
32
+
33
+ # Open the map file
34
+ with Image.open("tests/data/map_130.dat") as img:
35
+ # Convert from 8-bit color-indexed format ("P") to True Color ("RGB")
36
+ rgb_img = img.convert("RGB")
37
+
38
+ # Open inside your system's default image viewer
39
+ rgb_img.show()
40
+
41
+ # Export it to a standard format
42
+ rgb_img.save("minecraft_map_render.png")
43
+ ```
44
+
45
+ ### Accessing Map Metadata (`info`)
46
+
47
+ The plugin maps in-game positioning values into the image's `.info` dictionary:
48
+
49
+ ```python
50
+ from PIL import Image
51
+ import pillow_minecraft_map
52
+
53
+ with Image.open("tests/data/map_130.dat") as img:
54
+ x = img.info.get("x_center")
55
+ z = img.info.get("z_center")
56
+ scale_factor = img.info.get("scale")
57
+ version = img.info.get("data_version")
58
+
59
+ # Calculate real-world block coverage
60
+ block_width = 128 * (2 ** scale_factor)
61
+
62
+ print(f"Minecraft DataVersion: {version}")
63
+ print(f"Center Coordinates: X={x}, Z={z}")
64
+ print(f"Scale: 1:{2**scale_factor} ({block_width}x{block_width} blocks total)")
65
+ ```
66
+
67
+ ## License
68
+
69
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pillow-minecraft-map"
7
+ version = "1.0.0"
8
+ description = "A Pillow Image plugin to open Minecraft .dat map files."
9
+ readme = "README.md"
10
+ authors = [
11
+ { name = "Christopher Bruns", email = "cmbruns@rotatingpenguin.com" }
12
+ ]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ "Topic :: Multimedia :: Graphics",
18
+ ]
19
+ dependencies = [
20
+ "Pillow>=10.0.0"
21
+ ]
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/cmbruns/pillow-minecraft-map"
@@ -0,0 +1,4 @@
1
+ from .plugin import register_plugin
2
+
3
+ # Automatically register when imported
4
+ register_plugin()
@@ -0,0 +1,288 @@
1
+ """
2
+ Module pillow_minecraft_map
3
+
4
+ PIL plugin for loading a Minecraft map info file as an Image.
5
+ These files are usually stored in server files like "world/data/map_<id>.dat"
6
+ Minecraft Java Edition map info files are gzipped NBT files.
7
+
8
+ Limitations:
9
+ * Support for Minecraft Jave Edition map info files only. Bedrock Edition maps
10
+ are not currently supported.
11
+ """
12
+
13
+
14
+ import gzip
15
+ from PIL import Image, ImageFile, ImagePalette
16
+
17
+ # --- HISTORICAL MAP PALETTES ---
18
+ # --- STEP 1: Baseline 1.7 Legacy Map Palette (14 base IDs / 56 colors) ---
19
+ MINECRAFT_1_7_MAP_PALETTE = [
20
+ # ID 0: Air / Transparent
21
+ (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
22
+ # ID 1: Grass
23
+ (89, 125, 39), (109, 153, 48), (127, 178, 56), (67, 94, 29),
24
+ # ID 2: Sand
25
+ (174, 164, 115), (213, 201, 140), (247, 233, 163), (130, 123, 86),
26
+ # ID 3: Cloth / White Wool
27
+ (140, 140, 140), (171, 171, 171), (199, 199, 199), (105, 105, 105),
28
+ # ID 4: TNT / Fire / Lava
29
+ (180, 0, 0), (220, 0, 0), (255, 0, 0), (135, 0, 0),
30
+ # ID 5: Ice
31
+ (112, 112, 180), (138, 138, 220), (160, 160, 255), (84, 84, 135),
32
+ # ID 6: Iron / Metal
33
+ (117, 117, 117), (144, 144, 144), (167, 167, 167), (88, 88, 88),
34
+ # ID 7: Foliage / Plant
35
+ (0, 87, 0), (0, 106, 0), (0, 124, 0), (0, 65, 0),
36
+ # ID 8: Snow
37
+ (180, 180, 180), (220, 220, 220), (255, 255, 255), (135, 135, 135),
38
+ # ID 9: Clay
39
+ (115, 118, 129), (141, 145, 158), (164, 168, 184), (86, 88, 97),
40
+ # ID 10: Dirt
41
+ (106, 76, 54), (130, 94, 66), (151, 109, 77), (79, 57, 40),
42
+ # ID 11: Stone
43
+ (79, 79, 79), (96, 96, 96), (112, 112, 112), (59, 59, 59),
44
+ # ID 12: Water
45
+ (45, 45, 180), (55, 55, 220), (64, 64, 255), (33, 33, 135),
46
+ # ID 13: Wood
47
+ (100, 84, 50), (123, 102, 62), (143, 119, 72), (75, 63, 38)
48
+ ]
49
+
50
+ # --- STEP 2: Release 1.8 to 1.11 Map Palette (Adds 16 wool colors + precious blocks -> 35 IDs) ---
51
+ MINECRAFT_1_8_MAP_PALETTE = MINECRAFT_1_7_MAP_PALETTE + [
52
+ # ID 14: Quartz
53
+ (180, 177, 172), (220, 217, 211), (255, 252, 245), (135, 133, 129),
54
+ # ID 15: Orange Wool
55
+ (152, 89, 36), (186, 109, 44), (216, 127, 51), (114, 67, 27),
56
+ # ID 16: Magenta Wool
57
+ (125, 53, 152), (153, 65, 186), (178, 76, 216), (94, 40, 114),
58
+ # ID 17: Light Blue Wool
59
+ (72, 108, 152), (88, 132, 186), (102, 153, 216), (54, 81, 114),
60
+ # ID 18: Yellow Wool
61
+ (161, 161, 36), (197, 197, 44), (229, 229, 51), (121, 121, 27),
62
+ # ID 19: Lime Wool
63
+ (89, 144, 17), (109, 176, 21), (127, 204, 25), (67, 108, 13),
64
+ # ID 20: Pink Wool
65
+ (170, 89, 116), (208, 109, 142), (242, 127, 165), (128, 67, 87),
66
+ # ID 21: Gray Wool
67
+ (53, 53, 53), (65, 65, 65), (76, 76, 76), (40, 40, 40),
68
+ # ID 22: Light Gray Wool
69
+ (108, 108, 108), (132, 132, 132), (153, 153, 153), (81, 81, 81),
70
+ # ID 23: Cyan Wool
71
+ (53, 89, 108), (65, 109, 132), (76, 127, 153), (40, 67, 81),
72
+ # ID 24: Purple Wool
73
+ (89, 44, 125), (109, 54, 153), (127, 63, 178), (67, 33, 94),
74
+ # ID 25: Blue Wool
75
+ (36, 53, 125), (44, 65, 153), (51, 76, 178), (27, 40, 94),
76
+ # ID 26: Brown Wool
77
+ (72, 53, 36), (88, 65, 44), (102, 76, 51), (54, 40, 27),
78
+ # ID 27: Green Wool
79
+ (72, 89, 36), (88, 109, 44), (102, 127, 51), (54, 67, 27),
80
+ # ID 28: Red Wool
81
+ (108, 36, 36), (132, 44, 44), (153, 51, 51), (81, 27, 27),
82
+ # ID 29: Black Wool
83
+ (17, 17, 17), (21, 21, 21), (25, 25, 25), (13, 13, 13),
84
+ # ID 30: Gold
85
+ (176, 168, 54), (215, 205, 66), (250, 238, 77), (132, 126, 40),
86
+ # ID 31: Diamond
87
+ (64, 154, 150), (79, 189, 183), (92, 219, 213), (48, 116, 112),
88
+ # ID 32: Lapis
89
+ (52, 90, 180), (63, 110, 220), (74, 128, 255), (39, 67, 135),
90
+ # ID 33: Emerald
91
+ (0, 153, 40), (0, 187, 50), (0, 217, 58), (0, 114, 30),
92
+ # ID 34: Podzol
93
+ (91, 60, 34), (111, 74, 42), (129, 86, 49), (68, 45, 25),
94
+ # ID 35: Netherrack
95
+ (79, 1, 0), (96, 1, 0), (112, 2, 0), (59, 1, 0)
96
+ ]
97
+
98
+ # --- STEP 3: Release 1.12 to 1.15 Map Palette (Adds 16 Terracotta options -> 51 IDs) ---
99
+ MINECRAFT_1_12_MAP_PALETTE = MINECRAFT_1_8_MAP_PALETTE + [
100
+ # ID 36: White Terracotta
101
+ (147, 124, 113), (180, 152, 138), (209, 177, 161), (110, 93, 85),
102
+ # ID 37: Orange Terracotta
103
+ (112, 57, 25), (137, 70, 31), (159, 82, 36), (84, 43, 19),
104
+ # ID 38: Magenta Terracotta
105
+ (105, 61, 76), (128, 75, 93), (149, 87, 108), (78, 46, 57),
106
+ # ID 39: Light Blue Terracotta
107
+ (79, 76, 97), (96, 93, 119), (112, 108, 138), (59, 57, 73),
108
+ # ID 40: Yellow Terracotta
109
+ (131, 93, 25), (160, 114, 31), (186, 133, 36), (98, 70, 19),
110
+ # ID 41: Lime Terracotta
111
+ (72, 82, 37), (88, 100, 45), (103, 117, 53), (54, 61, 28),
112
+ # ID 42: Pink Terracotta
113
+ (112, 54, 55), (138, 66, 67), (160, 77, 78), (84, 40, 41),
114
+ # ID 43: Gray Terracotta
115
+ (40, 28, 24), (49, 35, 30), (57, 41, 35), (30, 21, 18),
116
+ # ID 44: Light Gray Terracotta
117
+ (95, 75, 69), (116, 92, 84), (135, 107, 98), (71, 56, 51),
118
+ # ID 45: Cyan Terracotta
119
+ (61, 64, 64), (75, 79, 79), (87, 92, 92), (46, 48, 48),
120
+ # ID 46: Purple Terracotta
121
+ (86, 51, 62), (105, 62, 75), (122, 73, 88), (64, 38, 46),
122
+ # ID 47: Blue Terracotta
123
+ (53, 43, 64), (65, 53, 79), (76, 62, 92), (40, 32, 48),
124
+ # ID 48: Brown Terracotta
125
+ (53, 35, 24), (65, 43, 30), (76, 50, 35), (40, 26, 18),
126
+ # ID 49: Green Terracotta
127
+ (53, 57, 29), (65, 70, 36), (76, 82, 42), (40, 43, 22),
128
+ # ID 50: Red Terracotta
129
+ (100, 42, 32), (122, 51, 39), (142, 60, 46), (75, 31, 24),
130
+ # ID 51: Black Terracotta
131
+ (26, 15, 11), (31, 18, 13), (37, 22, 16), (19, 11, 8)
132
+ ]
133
+
134
+ # --- STEP 4: Release 1.16 to 1.17 Map Palette (Adds Nether woods/nyliums -> 58 IDs) ---
135
+ MINECRAFT_1_16_MAP_PALETTE = MINECRAFT_1_12_MAP_PALETTE + [
136
+ # ID 52: Crimson Nylium
137
+ (133, 33, 34), (163, 41, 42), (189, 48, 49), (100, 25, 25),
138
+ # ID 53: Crimson Stem
139
+ (104, 44, 68), (127, 54, 83), (148, 63, 97), (78, 33, 51),
140
+ # ID 54: Crimson Hyphae
141
+ (64, 17, 20), (79, 21, 25), (92, 25, 29), (48, 13, 15),
142
+ # ID 55: Warped Nylium
143
+ (15, 88, 94), (18, 108, 115), (22, 126, 134), (11, 66, 71),
144
+ # ID 56: Warped Stem
145
+ (40, 100, 98), (50, 122, 120), (58, 142, 140), (30, 75, 74),
146
+ # ID 57: Warped Hyphae
147
+ (60, 31, 43), (74, 37, 53), (86, 44, 62), (45, 23, 32),
148
+ # ID 58: Warped Wart Block
149
+ (14, 127, 93), (17, 155, 114), (20, 180, 133), (10, 95, 70)
150
+ ]
151
+
152
+ # --- STEP 5: Release 1.18 to 1.19 Map Palette (Adds Deepslate, Raw Iron, Glow Lichen -> 61 IDs) ---
153
+ MINECRAFT_1_18_MAP_PALETTE = MINECRAFT_1_16_MAP_PALETTE + [
154
+ # ID 59: Deepslate
155
+ (70, 70, 70), (86, 86, 86), (100, 100, 100), (52, 52, 52),
156
+ # ID 60: Raw Iron
157
+ (152, 123, 103), (186, 150, 126), (216, 175, 147), (114, 92, 77),
158
+ # ID 61: Glow Lichen
159
+ (89, 117, 105), (109, 144, 129), (127, 167, 150), (67, 88, 79)
160
+ ]
161
+
162
+ # --- STEP 6: Release 1.20+ Map Palette (Adds Copper variants, Cherry Wood -> 65 IDs) ---
163
+ MINECRAFT_1_20_MAP_PALETTE = MINECRAFT_1_18_MAP_PALETTE + [
164
+ # ID 62: Verdigris / Oxidized Copper
165
+ (104, 154, 133), (127, 189, 163), (148, 219, 189), (78, 116, 100),
166
+ # ID 63: Terracotta Light Gray / Pale Purple
167
+ (79, 60, 52), (96, 74, 64), (112, 86, 75), (59, 45, 39),
168
+ # ID 64: Cherry Wood / Pink Tint
169
+ (149, 102, 116), (182, 124, 142), (211, 144, 165), (111, 76, 87),
170
+ # ID 65: Bogged / Mud / Mangrove Root
171
+ (58, 48, 31), (71, 59, 38), (83, 69, 45), (43, 36, 23)
172
+ ]
173
+
174
+
175
+ class MinecraftMapImageFile(ImageFile.ImageFile):
176
+ format = "MINECRAFT_MAP"
177
+ format_description = "Minecraft Map Item Format (.dat)"
178
+
179
+ @staticmethod
180
+ def accept(prefix: bytes) -> bool:
181
+ """
182
+ Quickly checks the first 4-16 bytes of a file to see
183
+ if it matches this plugin's format.
184
+ """
185
+ # Match only the standard GZIP magic numbers
186
+ return len(prefix) >= 3 and prefix[:3] == b"\x1f\x8b\x08"
187
+
188
+ def _find_tag_payload(self, tag: str) -> int:
189
+ tag_bytes = tag.encode()
190
+ nbt_string_prefix = len(tag_bytes).to_bytes(2, "big") + tag_bytes
191
+ return self.file_bytes.index(nbt_string_prefix) + len(nbt_string_prefix)
192
+
193
+ def get_byte(self, tag: str) -> int:
194
+ """Fetch an NBT single byte value by tag name."""
195
+ pos = self._find_tag_payload(tag)
196
+ assert isinstance(self.file_bytes, bytes)
197
+ return int.from_bytes(self.file_bytes[pos: pos + 1], "big", signed=True)
198
+
199
+ def get_byte_array(self, tag: str) -> bytes:
200
+ """Fetch an NBT byte array by tag name"""
201
+ pos = self._find_tag_payload(tag)
202
+ assert isinstance(self.file_bytes, bytes)
203
+ size = int.from_bytes(self.file_bytes[pos: pos + 4], "big")
204
+ return self.file_bytes[pos + 4: pos + 4 + size]
205
+
206
+ def get_int(self, tag: str) -> int:
207
+ """Fetch an NBT integer value by tag name"""
208
+ pos = self._find_tag_payload(tag)
209
+ assert isinstance(self.file_bytes, bytes)
210
+ return int.from_bytes(self.file_bytes[pos: pos + 4], "big", signed=True)
211
+
212
+ def load(self):
213
+ """Override load to push the bytes straight to the internal C core."""
214
+ if hasattr(self, "_pixels") and self._pixels is not None:
215
+ self.load_prepare()
216
+ self.frombytes(self._pixels, "raw", ("P", 0, 1))
217
+ if self.palette:
218
+ raw_mode, data_bytes = self.palette.getdata()
219
+ self.im.putpalette("RGB", raw_mode, data_bytes)
220
+ self._pixels = None
221
+ return super().load()
222
+
223
+ def _open(self) -> None:
224
+ """
225
+ Reads the file header, sets metadata, and loads the small pixel array.
226
+ """
227
+ # 1. Rewind the stream and open it as a GZIP stream
228
+ self.fp.seek(0)
229
+ with gzip.open(self.fp, mode="rb") as gz:
230
+ _fb = gz.read()
231
+ assert isinstance(_fb, bytes)
232
+ self.file_bytes: bytes = _fb
233
+ self._pixels = self.get_byte_array("colors")
234
+ assert 128*128 == len(self._pixels)
235
+ self._size = 128, 128
236
+ # Handle color palette
237
+ self._mode = "P"
238
+ try:
239
+ data_version = self.get_int("DataVersion")
240
+ except ValueError:
241
+ # Fallback layout if DataVersion string signature is absent in older files
242
+ data_version = 0
243
+ if data_version >= 3463: # Minecraft Java Edition 1.20+
244
+ raw_palette = MINECRAFT_1_20_MAP_PALETTE
245
+ elif data_version >= 2975: # 1.18 - 1.19
246
+ raw_palette = MINECRAFT_1_18_MAP_PALETTE
247
+ elif data_version >= 2566: # 1.16 - 1.17
248
+ raw_palette = MINECRAFT_1_16_MAP_PALETTE
249
+ elif data_version >= 1139: # 1.12 - 1.15
250
+ raw_palette = MINECRAFT_1_12_MAP_PALETTE
251
+ elif data_version >= 99: # 1.8 - 1.11
252
+ raw_palette = MINECRAFT_1_8_MAP_PALETTE
253
+ else: # Pre-1.8 Legacy Beta
254
+ raw_palette = MINECRAFT_1_7_MAP_PALETTE
255
+ # Flatten the list of RGB tuples into a 1D sequence of integers
256
+ flat_palette = [color for rgb in raw_palette for color in rgb]
257
+ # Pad out to exactly 768 entries (256 colors * 3 channels) using zeros
258
+ pil_palette = flat_palette + [0] * (768 - len(flat_palette))
259
+ self.palette = ImagePalette.ImagePalette(mode="RGB", palette=pil_palette)
260
+ # Parse metadata
261
+ try:
262
+ x_center = self.get_int("xCenter")
263
+ z_center = self.get_int("zCenter")
264
+ scale = self.get_byte("scale")
265
+ except ValueError:
266
+ # Safe fallbacks if parsing legacy/custom map structures
267
+ x_center, z_center, scale = 0, 0, 0
268
+ # Inject into Pillow's standard metadata API
269
+ self.info["x_center"] = x_center
270
+ self.info["z_center"] = z_center
271
+ self.info["scale"] = scale
272
+ self.info["data_version"] = data_version
273
+ # File handle is no longer needed now
274
+ self.fp = None
275
+
276
+
277
+ def register_plugin():
278
+ """Hooks the Minecraft decoder module into Pillow's driver registry."""
279
+ if MinecraftMapImageFile.format not in Image.ID:
280
+ Image.register_extensions(
281
+ MinecraftMapImageFile.format,
282
+ extensions=[".dat"],
283
+ )
284
+ Image.register_open(
285
+ MinecraftMapImageFile.format,
286
+ MinecraftMapImageFile,
287
+ MinecraftMapImageFile.accept,
288
+ )
@@ -0,0 +1,9 @@
1
+ from PIL import Image
2
+ import pillow_minecraft_map # "unused" import loads the plugin as a side effect
3
+
4
+ with Image.open("../tests/data/map_111.dat") as img:
5
+ assert img.mode == "P"
6
+ for tag in ["x_center", "z_center", "scale", "data_version"]:
7
+ val = img.info.get(tag)
8
+ print(f"{tag} = {val}")
9
+ img.show()