From b9469610e2a673d76d230896c4533d111857232d Mon Sep 17 00:00:00 2001 From: leo <80651389+civilwarinfrance@users.noreply.github.com> Date: Tue, 27 Oct 2026 12:55:23 -0700 Subject: [PATCH] Allow omitted image dimensions in TSX tilesets AI-assisted by Codex (GPT-6). --- pytiled_parser/parsers/tmx/tileset.py | 16 ++++--- tests/test_optional_image_dimensions.py | 58 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 tests/test_optional_image_dimensions.py diff --git a/pytiled_parser/parsers/tmx/tileset.py b/pytiled_parser/parsers/tmx/tileset.py index 9c34f975..fdf9896d 100644 --- a/pytiled_parser/parsers/tmx/tileset.py +++ b/pytiled_parser/parsers/tmx/tileset.py @@ -108,10 +108,12 @@ def _parse_tile( else: tile.image = Path(image_element.attrib["source"]) - tile.image_width = int(image_element.attrib["width"]) - tile.width = tile.image_width - tile.image_height = int(image_element.attrib["height"]) - tile.height = tile.image_height + if image_element.attrib.get("width") is not None: + tile.image_width = int(image_element.attrib["width"]) + tile.width = tile.image_width + if image_element.attrib.get("height") is not None: + tile.image_height = int(image_element.attrib["height"]) + tile.height = tile.image_height if raw_tile.attrib.get("x") is not None: tile.x = int(raw_tile.attrib["x"]) @@ -181,8 +183,10 @@ def parse( else: tileset.image = Path(image_element.attrib["source"]) - tileset.image_width = int(image_element.attrib["width"]) - tileset.image_height = int(image_element.attrib["height"]) + if image_element.attrib.get("width") is not None: + tileset.image_width = int(image_element.attrib["width"]) + if image_element.attrib.get("height") is not None: + tileset.image_height = int(image_element.attrib["height"]) if image_element.attrib.get("trans") is not None: my_string = image_element.attrib["trans"] diff --git a/tests/test_optional_image_dimensions.py b/tests/test_optional_image_dimensions.py new file mode 100644 index 00000000..d2a322e6 --- /dev/null +++ b/tests/test_optional_image_dimensions.py @@ -0,0 +1,58 @@ +"""Image dimensions are optional in TSX, just as in JSON tilesets.""" + +import json +import xml.etree.ElementTree as etree +from pathlib import Path + +import pytest + +from pytiled_parser import parse_tileset + + +@pytest.mark.parametrize("format_", ["json", "tsx"]) +@pytest.mark.parametrize("collection", [False, True]) +@pytest.mark.parametrize( + "dimensions", [{}, {"width": 32}, {"height": 16}, {"width": 32, "height": 16}] +) +def test_optional_image_dimensions(tmp_path, format_, collection, dimensions): + attributes = { + "name": "test", + "spacing": 0, + "margin": 0, + "tilecount": 1, + "tilewidth": 32, + "tileheight": 16, + "columns": 0 if collection else 1, + } + filename = tmp_path / ("tileset." + format_) + if format_ == "json": + image = {"image": "tile.png"} + image.update({"image" + key: value for key, value in dimensions.items()}) + if collection: + attributes["tiles"] = [dict(image, id=0)] + else: + attributes.update(image) + filename.write_text(json.dumps(attributes), encoding="utf-8") + else: + root = etree.Element( + "tileset", {key: str(value) for key, value in attributes.items()} + ) + parent = etree.SubElement(root, "tile", id="0") if collection else root + etree.SubElement( + parent, + "image", + source="tile.png", + **{key: str(value) for key, value in dimensions.items()}, + ) + etree.ElementTree(root).write(filename, encoding="utf-8") + + tileset = parse_tileset(filename) + image = tileset.tiles[0] if collection else tileset + assert image.image == Path("tile.png") + assert image.image_width == dimensions.get("width") + assert image.image_height == dimensions.get("height") + if collection: + assert image.width == dimensions.get("width", 0) + assert image.height == dimensions.get("height", 0) + assert tileset.tile_width == 32 + assert tileset.tile_height == 16