diff --git a/caption_filter.module b/caption_filter.module
index aaa061d..8b87bfe 100644
--- a/caption_filter.module
+++ b/caption_filter.module
@@ -51,8 +51,9 @@ function caption_filter_tips($filter, $format, $long = FALSE) {
  * Filter process callback. Replace the [caption] tags with HTML.
  */
 function caption_filter_process_filter($text, $filter) {
-  if (stristr($text, '[caption align=') !== FALSE || stristr($text, '[caption]') !== FALSE) { // prevents useless processing
-    $pattern = "/(\[(caption( align=(left|center|right))?)[^\]]*\])(.*?)(\[\/caption\])/";
+  // Prevent useless processing if there are no caption tags at all.
+  if (stristr($text, '[caption') !== FALSE) {
+    $pattern = "/(\[caption([^\]]*)\])(.*?)(\[\/caption\])/";
     $text = preg_replace_callback($pattern, '_caption_filter_replace', $text);
   }
   return $text;
@@ -65,16 +66,18 @@ function caption_filter_process_filter($text, $filter) {
  * HTML output.
  */
 function _caption_filter_replace($matches) {
-  $item = $matches[5];
-  $align = $matches[4];
+  $caption_attributes = _caption_filter_tag_attributes($matches[2]);
+  $item = $matches[3];
 
   $width = _caption_filter_get_width($item);
+  $align = isset($caption_attributes['align']) ? $caption_attributes['align'] : 'center';
 
-  // [caption] items do not have an alignment.
-  if (!$align) {
-    $align = 'center';
+  // Remove "align" from the start of the alignment if needed. WordPress
+  // commonly uses align="alignright" for example.
+  if (strpos($align, 'align') === 0) {
+    $align = substr($align, 5);
   }
-  
+
   return '<div class="caption caption-'. $align .'"><div class="caption-inner" style="width: '. $width .';">'. $item .'</div></div>';
 }
 
@@ -114,3 +117,49 @@ function _caption_filter_get_width($item) {
 
   return $width;
 }
+
+/**
+ * Retrieve all attributes from a caption "shortcode" tag.
+ *
+ * This code is based upon the WordPress function shortcode_parse_atts().
+ *
+ * @see http://core.svn.wordpress.org/branches/3.2/wp-includes/shortcodes.php
+ *
+ * @param $text
+ *   The shortcode tag attributes to be parsed. This should not include the
+ *   brackets, the tag itself, or any of the contents within the tag. It should
+ *   be a string of attributes, such as:
+ *
+ *   @code
+ *     attr1="value 1" attr2=value2 value3
+ *   @endcode
+ *
+ *   Note that these tags may or may not use quotes, either single or double.
+ * @return
+ *   An array of attributes and their value.
+ */
+function _caption_filter_tag_attributes($text) {
+  $atts = array();
+  $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
+  $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
+  if (preg_match_all($pattern, $text, $match, PREG_SET_ORDER)) {
+    foreach ($match as $m) {
+      if (!empty($m[1]))
+        $atts[strtolower($m[1])] = stripcslashes($m[2]);
+      elseif (!empty($m[3]))
+        $atts[strtolower($m[3])] = stripcslashes($m[4]);
+      elseif (!empty($m[5]))
+        $atts[strtolower($m[5])] = stripcslashes($m[6]);
+      // These two rules vary slightly from the WordPress source. Single word
+      // attributes (e.g "checked") are set to have the value of TRUE.
+      elseif (isset($m[7]) and strlen($m[7]))
+        $atts[strtolower($m[7])] = TRUE;
+      elseif (isset($m[8]))
+        $atts[strtolower($m[8])] = TRUE;
+    }
+  }
+  else {
+    $atts = ltrim($text);
+  }
+  return $atts;
+}
