diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index 7c463cc94d8f90cf6abb20274c685b8de60e3dab..bf5835fb343733c7b8a82ac8a0ed638f06b2b79d 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -3480,13 +3480,26 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
+	PUGI__FN void node_output_attributes(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth)
 	{
 		const char_t* default_name = PUGIXML_TEXT(":anonymous");
 
+		bool eachAttributeOnNewLine = PUGI__NODETYPE(node) != node_declaration
+			&& (flags & format_indent)
+			&& (flags & format_each_attribute_on_new_line)
+			&& (flags & format_raw) == 0;
+
 		for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
 		{
-			writer.write(' ');
+			if (eachAttributeOnNewLine)
+			{
+				writer.write('\n');
+				text_output_indent(writer, indent, indent_length, depth + 1);
+			}
+			else
+			{
+				writer.write(' ');
+			}
 			writer.write_string(a->name ? a->name : default_name);
 			writer.write('=', '"');
 
@@ -3497,7 +3510,7 @@ PUGI__NS_BEGIN
 		}
 	}
 
-	PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
+	PUGI__FN bool node_output_start(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth)
 	{
 		const char_t* default_name = PUGIXML_TEXT(":anonymous");
 		const char_t* name = node->name ? node->name : default_name;
@@ -3506,18 +3519,16 @@ PUGI__NS_BEGIN
 		writer.write_string(name);
 
 		if (node->first_attribute)
-			node_output_attributes(writer, node, flags);
+			node_output_attributes(writer, node, indent, indent_length, flags, depth);
 
 		if (!node->first_child)
 		{
 			writer.write(' ', '/', '>');
-
 			return false;
 		}
 		else
 		{
 			writer.write('>');
-
 			return true;
 		}
 	}
@@ -3532,7 +3543,7 @@ PUGI__NS_BEGIN
 		writer.write('>');
 	}
 
-	PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
+	PUGI__FN void node_output_simple(xml_buffered_writer &writer, xml_node_struct *node, const char_t *indent, size_t indent_length, unsigned int flags, unsigned int depth)
 	{
 		const char_t* default_name = PUGIXML_TEXT(":anonymous");
 
@@ -3566,7 +3577,7 @@ PUGI__NS_BEGIN
 			case node_declaration:
 				writer.write('<', '?');
 				writer.write_string(node->name ? node->name : default_name);
-				node_output_attributes(writer, node, flags);
+				node_output_attributes(writer, node, indent, indent_length, flags, depth);
 				writer.write('?', '>');
 				break;
 
@@ -3608,7 +3619,7 @@ PUGI__NS_BEGIN
 			// begin writing current node
 			if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata)
 			{
-				node_output_simple(writer, node, flags);
+				node_output_simple(writer, node, indent, indent_length, flags, depth);
 
 				indent_flags = 0;
 			}
@@ -3624,7 +3635,7 @@ PUGI__NS_BEGIN
 				{
 					indent_flags = indent_newline | indent_indent;
 
-					if (node_output_start(writer, node, flags))
+					if (node_output_start(writer, node, indent, indent_length, flags, depth))
 					{
 						node = node->first_child;
 						depth++;
@@ -3643,7 +3654,7 @@ PUGI__NS_BEGIN
 				}
 				else
 				{
-					node_output_simple(writer, node, flags);
+					node_output_simple(writer, node, indent, indent_length, flags, depth);
 
 					indent_flags = indent_newline | indent_indent;
 				}
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
index d59f8640398c513e423b37725afd450b4e1f76f0..36ed95518bce07d09b5f7024c858f9eb3338e725 100644
--- a/src/pugixml.hpp
+++ b/src/pugixml.hpp
@@ -203,9 +203,12 @@ namespace pugi
 	// Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.
 	const unsigned int format_save_file_text = 0x20;
 
+	// Set each attribute to new line
+	const unsigned int format_each_attribute_on_new_line = 0x40;
 	// The default set of formatting flags.
 	// Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
 	const unsigned int format_default = format_indent;
+	const unsigned int format_indent_attributes = format_indent | format_each_attribute_on_new_line;
 		
 	// Forward declarations
 	struct xml_attribute_struct;
diff --git a/tests/test_write.cpp b/tests/test_write.cpp
index af4acf46262b308fd2c0136908a0569f57897408..d2808403a3069ef678344b3b3ad28e94a7ebdca2 100644
--- a/tests/test_write.cpp
+++ b/tests/test_write.cpp
@@ -21,6 +21,21 @@ TEST_XML(write_indent, "<node attr='1'><child><sub>text</sub></child></node>")
 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n\t<child>\n\t\t<sub>text</sub>\n\t</child>\n</node>\n"), STR("\t"), format_indent);
 }
 
+TEST_XML(write_indent_attribute, "<node attr='1' other='2'><child><sub>text</sub></child></node>")
+{
+	CHECK_NODE_EX(doc, STR("<node\n\tattr=\"1\"\n\tother=\"2\">\n\t<child>\n\t\t<sub>text</sub>\n\t</child>\n</node>\n"), STR("\t"), format_indent_attributes);
+}
+
+TEST_XML(write_indent_attribute_empty_tag, "<node attr='1' other='2' />")
+{
+	CHECK_NODE_EX(doc, STR("<node\n\tattr=\"1\"\n\tother=\"2\" />\n"), STR("\t"), format_indent_attributes);
+}
+
+TEST_XML_FLAGS(write_indent_attribute_on_declaration, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><node attr='1' other='2' />", pugi::parse_full)
+{
+	CHECK_NODE_EX(doc, STR("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<node\n\tattr=\"1\"\n\tother=\"2\" />\n"), STR("\t"), format_indent_attributes);
+}
+
 TEST_XML(write_pcdata, "<node attr='1'><child><sub/>text</child></node>")
 {
 	CHECK_NODE_EX(doc, STR("<node attr=\"1\">\n\t<child>\n\t\t<sub />text</child>\n</node>\n"), STR("\t"), format_indent);
@@ -360,7 +375,7 @@ TEST(write_encoding_huge_invalid)
 TEST(write_unicode_escape)
 {
 	char s_utf8[] = "<\xE2\x82\xAC \xC2\xA2='\"\xF0\xA4\xAD\xA2&#x0a;\"'>&amp;\x14\xF0\xA4\xAD\xA2&lt;</\xE2\x82\xAC>";
-	
+
 	xml_document doc;
 	CHECK(doc.load_buffer(s_utf8, sizeof(s_utf8), parse_default, encoding_utf8));