diff --git a/src/map.rs b/src/map.rs
index f717d8b445c6eb6832bcbe92b3ca877b5ec1b060..e22ff65f977b797ed717b5095d287249e3ff5110 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -1,11 +1,4 @@
-use std::{
-    collections::HashMap,
-    fmt,
-    fs::File,
-    io::Read,
-    path::{Path, PathBuf},
-    str::FromStr,
-};
+use std::{collections::HashMap, fmt, fs::File, io::Read, path::Path, str::FromStr};
 
 use xml::{attribute::OwnedAttribute, reader::XmlEvent, EventReader};
 
diff --git a/src/tileset.rs b/src/tileset.rs
index 1c49c3a18df3ef22f322d15a7318e2a2b8dd297e..2b120ecd9b53f4c45085cb9d75c29a0ac7571f45 100644
--- a/src/tileset.rs
+++ b/src/tileset.rs
@@ -42,6 +42,20 @@ pub struct Tileset {
     pub source: Option<PathBuf>,
 }
 
+/// Internal structure for holding mid-parse information.
+struct TilesetProperties {
+    spacing: Option<u32>,
+    margin: Option<u32>,
+    tilecount: Option<u32>,
+    columns: Option<u32>,
+    first_gid: u32,
+    name: String,
+    tile_width: u32,
+    tile_height: u32,
+    path_relative_to: Option<PathBuf>,
+    source: Option<PathBuf>,
+}
+
 impl Tileset {
     /// Parse a buffer hopefully containing the contents of a Tiled tileset.
     ///
@@ -79,12 +93,45 @@ impl Tileset {
         })
     }
 
+    pub(crate) fn new_external<R: Read>(
+        file: R,
+        first_gid: u32,
+        path: Option<&Path>,
+    ) -> Result<Self, TiledError> {
+        let mut tileset_parser = EventReader::new(file);
+        loop {
+            match tileset_parser
+                .next()
+                .map_err(TiledError::XmlDecodingError)?
+            {
+                XmlEvent::StartElement {
+                    name, attributes, ..
+                } => {
+                    if name.local_name == "tileset" {
+                        return Self::parse_external_tileset(
+                            first_gid,
+                            &mut tileset_parser,
+                            &attributes,
+                            path,
+                        );
+                    }
+                }
+                XmlEvent::EndDocument => {
+                    return Err(TiledError::PrematureEnd(
+                        "Tileset Document ended before map was parsed".to_string(),
+                    ))
+                }
+                _ => {}
+            }
+        }
+    }
+
     fn parse_xml_embedded<R: Read>(
         parser: &mut EventReader<R>,
         attrs: &Vec<OwnedAttribute>,
         path_relative_to: Option<&Path>,
     ) -> Result<Tileset, TiledError> {
-        let ((spacing, margin, tilecount, columns), (first_gid, name, width, height)) = get_attrs!(
+        let ((spacing, margin, tilecount, columns), (first_gid, name, tile_width, tile_height)) = get_attrs!(
            attrs,
            optionals: [
                 ("spacing", spacing, |v:String| v.parse().ok()),
@@ -101,50 +148,21 @@ impl Tileset {
             TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
         );
 
-        let mut image = Option::None;
-        let mut tiles = Vec::new();
-        let mut properties = HashMap::new();
-        parse_tag!(parser, "tileset", {
-            "image" => |attrs| {
-                image = Some(Image::new(parser, attrs, path_relative_to.ok_or(TiledError::SourceRequired{object_to_parse: "Image".to_string()})?)?);
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-            "tile" => |attrs| {
-                tiles.push(Tile::new(parser, attrs, path_relative_to)?);
-                Ok(())
-            },
-        });
-
-        let columns = match columns {
-            Some(col) => col,
-            None => match &image {
-                None => {
-                    return Err(TiledError::MalformedAttributes(
-                        "No <image> nor columns attribute in <tileset>".to_string(),
-                    ))
-                }
-                Some(image) => image.width as u32 / width,
+        Self::finish_parsing_xml(
+            parser,
+            TilesetProperties {
+                spacing,
+                margin,
+                name,
+                path_relative_to: path_relative_to.map(Path::to_owned),
+                columns,
+                tilecount,
+                tile_height,
+                tile_width,
+                first_gid,
+                source: None,
             },
-        };
-
-        Ok(Tileset {
-            tile_width: width,
-            tile_height: height,
-            spacing: spacing.unwrap_or(0),
-            margin: margin.unwrap_or(0),
-            first_gid,
-            name,
-            tilecount,
-            columns,
-            image,
-            tiles,
-            properties,
-            source: None,
-        })
+        )
     }
 
     fn parse_xml_reference(
@@ -175,46 +193,13 @@ impl Tileset {
         Tileset::new_external(file, first_gid, Some(&tileset_path))
     }
 
-    pub(crate) fn new_external<R: Read>(
-        file: R,
-        first_gid: u32,
-        path: Option<&Path>,
-    ) -> Result<Self, TiledError> {
-        let mut tileset_parser = EventReader::new(file);
-        loop {
-            match tileset_parser
-                .next()
-                .map_err(TiledError::XmlDecodingError)?
-            {
-                XmlEvent::StartElement {
-                    name, attributes, ..
-                } => {
-                    if name.local_name == "tileset" {
-                        return Self::parse_external_tileset(
-                            first_gid,
-                            &mut tileset_parser,
-                            &attributes,
-                            path,
-                        );
-                    }
-                }
-                XmlEvent::EndDocument => {
-                    return Err(TiledError::PrematureEnd(
-                        "Tileset Document ended before map was parsed".to_string(),
-                    ))
-                }
-                _ => {}
-            }
-        }
-    }
-
     fn parse_external_tileset<R: Read>(
         first_gid: u32,
         parser: &mut EventReader<R>,
         attrs: &Vec<OwnedAttribute>,
         path: Option<&Path>,
     ) -> Result<Tileset, TiledError> {
-        let ((spacing, margin, tilecount, columns), (name, width, height)) = get_attrs!(
+        let ((spacing, margin, tilecount, columns), (name, tile_width, tile_height)) = get_attrs!(
             attrs,
             optionals: [
                 ("spacing", spacing, |v:String| v.parse().ok()),
@@ -230,14 +215,35 @@ impl Tileset {
             TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
         );
 
-        let source_path = path.and_then(|p| p.parent());
+        let source_path = path.and_then(|p| p.parent().map(Path::to_owned));
 
+        Self::finish_parsing_xml(
+            parser,
+            TilesetProperties {
+                spacing,
+                margin,
+                name,
+                path_relative_to: source_path,
+                columns,
+                tilecount,
+                tile_height,
+                tile_width,
+                first_gid,
+                source: path.map(Path::to_owned),
+            },
+        )
+    }
+
+    fn finish_parsing_xml<R: Read>(
+        parser: &mut EventReader<R>,
+        prop: TilesetProperties,
+    ) -> Result<Self, TiledError> {
         let mut image = Option::None;
         let mut tiles = Vec::new();
         let mut properties = HashMap::new();
         parse_tag!(parser, "tileset", {
             "image" => |attrs| {
-                image = Some(Image::new(parser, attrs, source_path.ok_or(TiledError::SourceRequired{object_to_parse: "Image".to_string()})?)?);
+                image = Some(Image::new(parser, attrs, prop.path_relative_to.as_ref().ok_or(TiledError::SourceRequired{object_to_parse: "Image".to_string()})?)?);
                 Ok(())
             },
             "properties" => |_| {
@@ -245,36 +251,45 @@ impl Tileset {
                 Ok(())
             },
             "tile" => |attrs| {
-                tiles.push(Tile::new(parser, attrs, path)?);
+                tiles.push(Tile::new(parser, attrs, prop.path_relative_to.as_ref().and_then(|p| Some(p.as_path())))?);
                 Ok(())
             },
         });
 
-        let columns = match columns {
-            Some(col) => col,
-            None => match &image {
-                None => {
-                    return Err(TiledError::MalformedAttributes(
-                        "No <image> and no <columns> in <tileset>".to_string(),
-                    ))
-                }
-                Some(image) => image.width as u32 / width,
-            },
-        };
+        let (margin, spacing) = (prop.margin.unwrap_or(0), prop.spacing.unwrap_or(0));
+
+        let columns = prop
+            .columns
+            .map(Ok)
+            .unwrap_or_else(|| Self::calculate_columns(&image, prop.tile_width, margin, spacing))?;
 
         Ok(Tileset {
-            first_gid: first_gid,
-            name: name,
-            tile_width: width,
-            tile_height: height,
-            spacing: spacing.unwrap_or(0),
-            margin: margin.unwrap_or(0),
+            first_gid: prop.first_gid,
+            name: prop.name,
+            tile_width: prop.tile_width,
+            tile_height: prop.tile_height,
+            spacing,
+            margin,
             columns,
-            tilecount,
+            tilecount: prop.tilecount,
             image,
-            tiles: tiles,
+            tiles,
             properties,
-            source: path.and_then(|x| Some(x.to_owned())),
+            source: prop.source,
         })
     }
+
+    fn calculate_columns(
+        image: &Option<Image>,
+        tile_width: u32,
+        margin: u32,
+        spacing: u32,
+    ) -> Result<u32, TiledError> {
+        image
+            .as_ref()
+            .ok_or(TiledError::MalformedAttributes(
+                "No <image> nor columns attribute in <tileset>".to_string(),
+            ))
+            .and_then(|image| Ok((image.width as u32 - margin + spacing) / (tile_width + spacing)))
+    }
 }