Index: django/template/__init__.py
===================================================================
--- django/template/__init__.py	(revision 12122)
+++ django/template/__init__.py	(working copy)
@@ -90,9 +90,13 @@
 UNKNOWN_SOURCE="&lt;unknown source&gt;"
 
 # match a variable or block tag and capture the entire tag, including start/end delimiters
-tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
-                                          re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
-                                          re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
+tag_re = re.compile('([\n\r\v\f]+[ \t]*?%s[^\n\r\v\f%s%s]*?%s(?=[ \t]*?[\n\r\v\f]+)|%s.*?%s|%s.*?%s|%s.*?%s)' % (
+                        re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), re.escape(BLOCK_TAG_END),
+                        re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),                        
+                        re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
+                        re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
+# find the command within a block tag
+block_command_re = re.compile('%s(?P<token_string>.*?)%s' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END)))
 
 # global dictionary of libraries that have been loaded using get_library
 libraries = {}
@@ -207,7 +211,7 @@
             self.contents[:20].replace('\n', ''))
 
     def split_contents(self):
-        split = []
+        split = []              
         bits = iter(smart_split(self.contents))
         for bit in bits:
             # Handle translation-marked template pieces
@@ -230,7 +234,7 @@
         "Return a list of tokens from a given template_string."
         in_tag = False
         result = []
-        for bit in tag_re.split(self.template_string):
+        for bit in tag_re.split(self.template_string): 
             if bit:
                 result.append(self.create_token(bit, in_tag))
             in_tag = not in_tag
@@ -241,12 +245,14 @@
         Convert the given token string into a new Token object and return it.
         If in_tag is True, we are processing something that matched a tag,
         otherwise it should be treated as a literal string.
-        """
+        """               
         if in_tag:
             if token_string.startswith(VARIABLE_TAG_START):
                 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip())
-            elif token_string.startswith(BLOCK_TAG_START):
-                token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip())
+            elif token_string.lstrip().startswith(BLOCK_TAG_START):
+                token_struct = block_command_re.search(token_string)
+                token_string = token_struct.group('token_string')
+                token = Token(TOKEN_BLOCK, token_string.strip())
             elif token_string.startswith(COMMENT_TAG_START):
                 token = Token(TOKEN_COMMENT, '')
         else:
@@ -984,4 +990,4 @@
     builtins.append(get_library(module_name))
 
 add_to_builtins('django.template.defaulttags')
-add_to_builtins('django.template.defaultfilters')
+add_to_builtins('django.template.defaultfilters')
\ No newline at end of file
Index: tests/regressiontests/templates/tests.py
===================================================================
--- tests/regressiontests/templates/tests.py	(revision 12122)
+++ tests/regressiontests/templates/tests.py	(working copy)
@@ -965,7 +965,18 @@
             'templatetag10': ('{% templatetag closebrace %}{% templatetag closebrace %}', {}, '}}'),
             'templatetag11': ('{% templatetag opencomment %}', {}, '{#'),
             'templatetag12': ('{% templatetag closecomment %}', {}, '#}'),
-
+            
+            # WHITESPACE TESTING
+            # Tags on their own line should collapse the newline before them
+            'templatetag-whitespace01': ('\n {% templatetag openblock %}\n', {}, '{%\n'),
+            # Tags that start on a newline, but have content thereafter, should not collapse the newline before or after them.
+            'templatetag-whitespace02': ('\n {% templatetag openblock %} String', {}, '\n {% String'),
+            'templatetag-whitespace03': ('\n {% templatetag openblock %} String \n', {}, '\n {% String \n'),
+            'templatetag-whitespace04': ('\n {% templatetag openbrace %}\nString\n{% templatetag closebrace %}\n', {}, '{\nString}\n'),
+            'templatetag-whitespace05': ('\n {% templatetag openbrace %}String{% templatetag closebrace %}\n', {}, '\n {String}\n'),
+            'templatetag-whitespace06': ('\n {% templatetag openbrace %}String{% templatetag closebrace %}', {}, '\n {String}'),
+            'templatetag-whitespace07': (' {% templatetag openbrace %}String{% templatetag closebrace %}', {}, ' {String}'),
+            
             ### WIDTHRATIO TAG ########################################################
             'widthratio01': ('{% widthratio a b 0 %}', {'a':50,'b':100}, '0'),
             'widthratio02': ('{% widthratio a b 100 %}', {'a':0,'b':0}, ''),
@@ -1082,6 +1093,7 @@
             # implementation details (fortunately, the (no)autoescape block
             # tags can be used in those cases)
             'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError),
+            
         }
 
 if __name__ == "__main__":
