diff --git a/src/item/PlacedSymbol.js b/src/item/PlacedSymbol.js
index 1bea7c0e..0b5e1e19 100644
--- a/src/item/PlacedSymbol.js
+++ b/src/item/PlacedSymbol.js
@@ -19,8 +19,6 @@ var PlacedSymbol = this.PlacedSymbol = Item.extend({
 		} else {
 			this.matrix = new Matrix();
 		}
-		// TODO: should size be cached here, or on Symbol?
-		this._size = this.symbol.getDefinition().getStrokeBounds().getSize();
 	},
 
 	_transform: function(matrix, flags) {
@@ -28,17 +26,14 @@ var PlacedSymbol = this.PlacedSymbol = Item.extend({
 		// raster, simply preconcatenate the internal matrix with the provided
 		// one.
 		this.matrix.preConcatenate(matrix);
-		this._bounds = null;
 	},
 
 	getBounds: function() {
-		// TODO: Is this right here? Shouldn't we calculate the bounds of the
-		// symbol transformed by this.matrix?
-		if (!this._bounds) {
-			this._bounds = this.matrix.transformBounds(
-					new Rectangle(this._size).setCenter(0, 0));
-		}
-		return this._bounds;
+		return this.symbol._definition.getStrokeBounds(this.matrix);
+	},
+
+	getStrokeBounds: function() {
+		return this.getBounds();
 	},
 
 	draw: function(ctx, param) {
diff --git a/src/path/Path.js b/src/path/Path.js
index 5dea700e..f99a57a8 100644
--- a/src/path/Path.js
+++ b/src/path/Path.js
@@ -70,41 +70,10 @@ var Path = this.Path = PathItem.extend({
 	// taken into account.
 
 	_transform: function(matrix, flags) {
-		var coords = new Array(6);
-		for (var i = 0, l = this._segments.length; i < l; i++) {
-			var segment = this._segments[i];
-			// Use matrix.transform version() that takes arrays of multiple
-			// points for largely improved performance, as no calls to
-			// Point.read() and Point constructors are necessary.
-			var point = segment._point,
-				handleIn = segment.getHandleInIfSet(),
-				handleOut = segment.getHandleOutIfSet(),
-				x = point.x,
-				y = point.y;
-			coords[0] = x;
-			coords[1] = y;
-			var index = 2;
-			// We need to convert handles to absolute coordinates in order
-			// to transform them.
-			if (handleIn) {
-				coords[index++] = handleIn.x + x;
-				coords[index++] = handleIn.y + y;
-			}
-			if (handleOut) {
-				coords[index++] = handleOut.x + x;
-				coords[index++] = handleOut.y + y;
-			}
-			matrix.transform(coords, 0, coords, 0, index / 2);
-			x = point.x = coords[0];
-			y = point.y = coords[1];
-			index  = 2;
-			if (handleIn) {
-				handleIn.x = coords[index++] - x;
-				handleIn.y = coords[index++] - y;
-			}
-			if (handleOut) {
-				handleOut.x = coords[index++] - x;
-				handleOut.y = coords[index++] - y;
+		if (!matrix.isIdentity()) {
+			var coords = new Array(6);
+			for (var i = 0, l = this._segments.length; i < l; i++) {
+				this._segments[i]._transformCoordinates(matrix, coords, true);
 			}
 		}
 	},
@@ -215,27 +184,32 @@ var Path = this.Path = PathItem.extend({
 		tMin = epsilon,
 		tMax = 1 - epsilon;
 
-	function calculateBounds(that, strokeRadius) {
+	function calculateBounds(that, matrix, strokePadding) {
 		// Code ported and further optimised from:
 		// http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
-		var segments = that._segments, first = segments[0];
+		var segments = that._segments,
+			first = segments[0];
 		if (!first)
 			return null;
-		var min = first._point.clone(),
-			max = min.clone(),
-			prev = first,
-			coords = ['x', 'y'];
+		var coords = new Array(6),
+			prevCoords = new Array(6);
+		// Make coordinates for first segment available in prevCoords.
+		if (matrix && matrix.isIdentity())
+			matrix = null;
+		first._transformCoordinates(matrix, prevCoords, false);
+		var min = prevCoords.slice(0, 2),
+			max = min.slice(0), // clone
 		function processSegment(segment) {
-			for (var i = 0; i < 2; i++) {
-				var coord = coords[i];
+			segment._transformCoordinates(matrix, coords, false);
 
-				var v0 = prev._point[coord],
-					v1 = v0 + prev._handleOut[coord],
-					v3 = segment._point[coord],
-					v2 = v3 + segment._handleIn[coord];
+			for (var i = 0; i < 2; i++) {
+				var v0 = prevCoords[i], // prev.point
+					v1 = prevCoords[i + 4], // prev.handleOut
+					v2 = coords[i + 2], // segment.handleIn
+					v3 = coords[i]; // segment.point
 
 				function add(value, t) {
-					var radius = 0;
+					var padding = 0;
 					if (value == null) {
 						// Calculate bezier polynomial at t
 						var u = 1 - t;
@@ -246,14 +220,14 @@ var Path = this.Path = PathItem.extend({
 						// Only add strokeWidth to bounds for points which lie
 						// within 0 < t < 1. The corner cases for cap and join
 						// are handled in getStrokeBounds()
-						radius = strokeRadius;
+						padding = strokePadding ? strokePadding[i] : 0;
 					}
-					var left = value - radius,
-						right = value + radius;
-					if (left < min[coord])
-						min[coord] = left;
-					if (right > max[coord])
-						max[coord] = right;
+					var left = value - padding,
+						right = value + padding;
+					if (left < min[i])
+						min[i] = left;
+					if (right > max[i])
+						max[i] = right;
 					
 				}
 				add(v3, null);
@@ -290,13 +264,16 @@ var Path = this.Path = PathItem.extend({
 				if (tMin < t2 && t2 < tMax)
 					add(null, t2);
 			}
-			prev = segment;
+			// Swap coordinate buffers
+			var tmp = prevCoords;
+			prevCoords = coords;
+			coords = tmp;
 		}
 		for (var i = 1, l = segments.length; i < l; i++)
 			processSegment(segments[i]);
 		if (that.closed)
 			processSegment(first);
-	    return new Rectangle(min.x, min.y, max.x - min.x , max.y - min.y);
+	    return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
 	}
 
 	/**
@@ -338,16 +315,17 @@ var Path = this.Path = PathItem.extend({
 		/**
 		 * The bounding rectangle of the item excluding stroke width.
 		 */
-		getBounds: function() {
-			return calculateBounds(this, 0);
+		getBounds: function(matrix) {
+			return calculateBounds(this, matrix);
 		},
 
 		/**
 		 * The bounding rectangle of the item including stroke width.
 		 */
-		getStrokeBounds: function() {
+		getStrokeBounds: function(matrix) {
 			var width = this.getStrokeWidth(),
 				radius = width / 2,
+				padding = [radius, radius],
 				join = this.getStrokeJoin(),
 				cap = this.getStrokeCap(),
 				// miter is relative to width. Divide it by 2 since we're
@@ -356,13 +334,60 @@ var Path = this.Path = PathItem.extend({
 				segments = this._segments,
 				length = segments.length,
 				closed= this.closed,
-				bounds = calculateBounds(this, radius);
+				bounds = calculateBounds(this, matrix, padding);
+
+			// If a matrix is provided, we need to rotate the stroke circle
+			// and calculate the bounding box of the resulting rotated elipse:
+			if (matrix) {
+				// Get rotated hor and ver vectors, and determine rotation angle
+				// and elipse values from them:
+				var mx = matrix.createShiftless(),
+					hor = mx.transform(new Point(radius, 0)),
+					ver = mx.transform(new Point(0, radius)),
+					phi = hor.getAngleInRadians(),
+					a = hor.getLength(),
+					b = ver.getLength();
+				// Formula for rotated ellipses:
+				// x = cx + a*cos(t)*cos(phi) - b*sin(t)*sin(phi)
+				// y = cy + b*sin(t)*cos(phi) + a*cos(t)*sin(phi)
+				// Derivates (by Wolfram Alpha):
+				// derivative of x = cx + a*cos(t)*cos(phi) - b*sin(t)*sin(phi)
+				// dx/dt = a sin(t) cos(phi) + b cos(t) sin(phi) = 0
+				// derivative of y = cy + b*sin(t)*cos(phi) + a*cos(t)*sin(phi)
+				// dy/dt = b cos(t) cos(phi) - a sin(t) sin(phi) = 0
+				// this can be simplified to:
+				// tan(t) = -b * tan(phi) / a // x
+				// tan(t) = b * cot(phi) / a // y
+				// Solving for t gives:
+				// t = pi * n - arctan(b tan(phi)) // x
+				// t = pi * n + arctan(b cot(phi)) // y
+				var tx = - Math.atan(b * Math.tan(phi)),
+					ty = + Math.atan(b / Math.tan(phi)),
+					// Due to symetry, we don't need to cycle through pi * n
+					// solutions:
+					x = a * Math.cos(tx) * Math.cos(phi),
+						- b * Math.sin(tx) * Math.sin(phi),
+					y = b * Math.sin(ty) * Math.cos(phi)
+						+ a * Math.cos(ty) * Math.sin(phi);
+				// Now update the join / round padding, as required by
+				// calculateBounds() and code below.
+				padding = [Math.abs(x), Math.abs(y)];
+			}
+
+			// Create a rectangle of padding size, used for union with bounds
+			// further down
+			var joinBounds = new Rectangle(new Size(padding).multiply(2));
+
+			function add(point) {
+				bounds = bounds.include(matrix
+					? matrix.transform(point) : point);
+			}
 
 			function addBevelJoin(curve, t) {
 				var point = curve.getPoint(t),
 					normal = curve.getNormal(t).normalize(radius);
-				bounds = bounds.include(point.add(normal));
-				bounds = bounds.include(point.subtract(normal));
+				add(point.add(normal));
+				add(point.subtract(normal));
 			}
 
 			function addJoin(segment, join) {
@@ -371,8 +396,8 @@ var Path = this.Path = PathItem.extend({
 				// When both handles are set in a segment, the join setting is
 				// ignored and round is always used.
 				if (join == 'round' || handleIn && handleOut) {
-					bounds = bounds.unite(new Rectangle(new Size(width, width))
-							.setCenter(segment._point));
+					bounds = bounds.unite(joinBounds.setCenter(matrix
+						? matrix.transform(segment._point) : segment._point));
 				} else {
 					switch (join) {
 					case 'bevel':
@@ -397,7 +422,7 @@ var Path = this.Path = PathItem.extend({
 						if (!corner || point.getDistance(corner) > miter) {
 							addJoin(segment, 'bevel');
 						} else {
-							bounds = bounds.include(corner);
+							add(corner);
 						}
 						break;
 					}
@@ -418,8 +443,8 @@ var Path = this.Path = PathItem.extend({
 					// direction of the tangent, which is the rotated normal
 					if (cap == 'square')
 						point = point.add(normal.y, -normal.x);
-					bounds = bounds.include(point.add(normal));
-					bounds = bounds.include(point.subtract(normal));
+					add(point.add(normal));
+					add(point.subtract(normal));
 					break;
 				}
 			}
diff --git a/src/path/Segment.js b/src/path/Segment.js
index 0f1c0bbb..065ef089 100644
--- a/src/path/Segment.js
+++ b/src/path/Segment.js
@@ -137,5 +137,63 @@ var Segment = this.Segment = Base.extend({
 				+ (this._handleOut.isZero()
 					? ', handleOut: ' + this._handleOut : '')
 				+ ' }';
+	},
+
+	_transformCoordinates: function(matrix, coords, change) {
+		// Use matrix.transform version() that takes arrays of multiple
+		// points for largely improved performance, as no calls to
+		// Point.read() and Point constructors are necessary.
+		var point = this._point,
+			// If a matrix is defined, only transform handles if they are set.
+			// This saves some computation time. If no matrix is set, always
+			// use the real handles, as we just want to receive a filled 
+			// coords array for _calculateBounds().
+			handleIn =  matrix && this.getHandleInIfSet() || this._handleIn,
+			handleOut = matrix && this.getHandleOutIfSet() || this._handleOut,
+			x = point.x,
+			y = point.y;
+		coords[0] = x;
+		coords[1] = y;
+		var index = 2;
+		// We need to convert handles to absolute coordinates in order
+		// to transform them.
+		if (handleIn) {
+			coords[index++] = handleIn.x + x;
+			coords[index++] = handleIn.y + y;
+		}
+		if (handleOut) {
+			coords[index++] = handleOut.x + x;
+			coords[index++] = handleOut.y + y;
+		}
+		if (matrix) {
+			matrix.transform(coords, 0, coords, 0, index / 2);
+			x = coords[0];
+			y = coords[1];
+			if (change) {
+				// If change is true, we need to set the new values back
+				point.x = x;
+				point.y = y;
+				index  = 2;
+				if (handleIn) {
+					handleIn.x = coords[index++] - x;
+					handleIn.y = coords[index++] - y;
+				}
+				if (handleOut) {
+					handleOut.x = coords[index++] - x;
+					handleOut.y = coords[index++] - y;
+				}
+			} else {
+				// We want to receive the results in coords, so make sure
+				// handleIn and out are defined too, even if they're 0
+				if (!handleIn) {
+					coords[index++] = x;
+					coords[index++] = y;
+				}
+				if (!handleOut) {
+					coords[index++] = x;
+					coords[index++] = y;
+				}
+			}
+		}
 	}
 });
diff --git a/test/tests/PlacedSymbol.js b/test/tests/PlacedSymbol.js
index a6aa3ab3..b1c0a254 100644
--- a/test/tests/PlacedSymbol.js
+++ b/test/tests/PlacedSymbol.js
@@ -18,13 +18,13 @@ test('placedSymbol bounds', function() {
 		new Rectangle(-50.5, -50.5, 101, 101),
 		'PlacedSymbol initial bounds.');
 	
-	placedSymbol.scale(0.5);
+	placedSymbol.scale(1, 0.5);
 	compareRectangles(placedSymbol.bounds,
-		{ x: -25.25, y: -25.25, width: 50.5, height: 50.5 },
+		{ x: -50.5, y: -25.25, width: 101, height: 50.5 },
 		'Bounds after scale.');
-	
+
 	placedSymbol.rotate(40);
 	compareRectangles(placedSymbol.bounds,
-		{ x: -25.50049, y: -25.50049, width: 51.00098, height: 51.00098 },
+		{ x: -42.04736, y: -37.91846, width: 84.09473, height: 75.83691 },
 		'Bounds after rotation.');
 });