diff --git a/src/edown_layout.erl b/src/edown_layout.erl
index f33ee16262032e9ca0deb36610bb1186b176dd61..55e289e4f079cb2ae541ad2d12db5d71b373556f 100644
--- a/src/edown_layout.erl
+++ b/src/edown_layout.erl
@@ -185,9 +185,7 @@ layout_module(#xmlElement{name = module, content = Es}=E, Opts) ->
     Body = ([]   % navigation("top")
             ++ [{h1, Title}]
 	    ++ doc_index(FullDesc, Functions, Types)
-	    ++ [{p,[]}]
 	    ++ ShortDesc
-	    ++ [{p,[]}]
 	    ++ copyright(Es)
 	    ++ deprecated(Es, "module")
 	    ++ version(Es)
@@ -219,8 +217,7 @@ layout_module(#xmlElement{name = module, content = Es}=E, Opts) ->
     %% 	    io:fwrite("not edown_doclet (~p)~n", [Name])
     %% end,
     %% xhtml(Title, stylesheet(Opts), Body).
-    Res = to_simple(markdown(Title, stylesheet(Opts), Body)),
-    Res.
+    to_simple(markdown(Title, stylesheet(Opts), Body)).
 
 %% This function is a workaround for a bug in xmerl_lib:expand_content/1 that
 %% causes it to lose track of the parents if #xmlElement{} records are
@@ -1268,9 +1265,10 @@ ot_name([E]) ->
 
 get_first_sentence([#xmlElement{name = p, content = Es} | Tail]) ->
     %% Descend into initial paragraph.
+    Tail1 = drop_empty_lines(Tail),
     {First, Rest} = get_first_sentence_1(Es),
     {First,
-     [#xmlElement{name = p, content = Rest} || Rest =/= []] ++ Tail};
+     [#xmlElement{name = p, content = Rest} || Rest =/= []] ++ Tail1};
 get_first_sentence(Es) ->
     get_first_sentence_1(Es).
 
@@ -1290,7 +1288,8 @@ get_first_sentence_1([E = #xmlText{value = Txt} | Es], Acc) ->
 	     if Rest == [] ->
 		     Es;
 		true ->
-		     [#xmlText{value=Rest} | Es]
+		     [#xmlText{value=trim_leading_lines(
+				       normalize_text(Rest))} | Es]
 	     end};
 	none ->
 	    get_first_sentence_1(Es, [E | Acc])
@@ -1298,8 +1297,10 @@ get_first_sentence_1([E = #xmlText{value = Txt} | Es], Acc) ->
 get_first_sentence_1([E | Es], Acc) ->
     % Skip non-text segments - don't descend further
     get_first_sentence_1(Es, [E | Acc]);
+get_first_sentence_1([], []) ->
+    {[], []};
 get_first_sentence_1([], Acc) ->
-    {lists:reverse(Acc), []}.
+    {{p, lists:reverse(Acc)}, []}.
 
 end_of_sentence(Cs, Last) ->
     end_of_sentence(Cs, Last, []).
@@ -1323,3 +1324,17 @@ end_of_sentence_1(C, Cs, true, As) ->
     {value, lists:reverse([C | As]), Cs};
 end_of_sentence_1(_, _, false, _) ->
     none.
+
+drop_empty_lines([#xmlText{value = Txt}=H|T]) ->
+    case trim_leading_lines(normalize_text(Txt)) of
+	[] ->
+	    drop_empty_lines(T);
+	Rest ->
+	    [H#xmlText{value = Rest}|T]
+    end.
+
+trim_leading_lines([H|T]) when H==$\n; H==$\t; H==$\s ->
+    trim_leading_lines(T);
+trim_leading_lines(Str) ->
+    Str.
+
diff --git a/src/edown_xmerl.erl b/src/edown_xmerl.erl
index c11e80847b5dfa6140c47d52c87900974396b5ed..aa01c23820b88f08b88fe3cf39d12f38863c592f 100644
--- a/src/edown_xmerl.erl
+++ b/src/edown_xmerl.erl
@@ -50,21 +50,23 @@ to_string(S) ->
     unicode:characters_to_list([S]).
 
 strip(Str) -> lstrip(rstrip(Str)).
-lstrip(Str) -> re:replace(Str,"^\\s","", [unicode]).
-rstrip(Str) -> re:replace(Str, "\\s\$", "", [unicode]).
+lstrip(Str) -> re:replace(Str,"^\\s","", [unicode,{return, list}]).
+rstrip(Str) -> re:replace(Str, "\\s\$", "", [unicode, {return, list}]).
 
 % Strip double spaces at end of line -- markdown reads as hard return.
-brstrip(Str) -> re:replace(Str, "\\s+\\s\$", "", [global, multiline, unicode]).
+brstrip(Str) -> re:replace(Str, "\\s+\\s\$", "", [global, multiline, unicode,
+						  {return, list}]).
 
 %% The '#root#' tag is called when the entire structure has been
 %% exported. It does not appear in the structure itself.
 
 '#root#'(Data, Attrs, [], _E) ->
+    Data1 = replace_edown_p(Data),
     case find_attribute(header, Attrs) of
 	{value, Hdr} ->
-	    [lists:flatten(io_lib:fwrite("HEADER: ~p~n", [Hdr])), Data];
+	    [lists:flatten(io_lib:fwrite("HEADER: ~p~n", [Hdr])), Data1];
 	false ->
-	    Data
+	    Data1
     end.
 
 %% Note that SGML does not have the <Tag/> empty-element form.
@@ -195,7 +197,7 @@ md_elem(Tag, Data, Attrs, Parents, E) ->
 	'div' -> Data;
 	ul    -> Data;
 	ol    -> Data;
-	p     -> ["\n", Data, "\n"];
+	p     -> ["<edown_p>", Data, "<edown_p>"];  % no need to use closing tag
 	b     -> ["__", no_nl(Data), "__"];
 	em    -> ["_", no_nl(Data), "_"];
 	i     -> ["_", no_nl(Data), "_"];
@@ -263,6 +265,23 @@ no_nl(S) ->
     string:strip([C || C <- to_string(S),
 		       C =/= $\n], both).
 
+replace_edown_p(Data) ->
+    Data1 = binary_to_list(iolist_to_binary(Data)),
+    io:fwrite("Data1 = ~p~n", [Data1]),
+    replace_edown_p(Data1, []).
+
+replace_edown_p("<edown_p>" ++ Data, Acc) ->
+    case lstrip(Data) of
+	"<edown_p>" ++ _ = Data1 ->
+	    replace_edown_p(Data1, Acc);
+	Data1 ->
+	    replace_edown_p(Data1, "\n\n" ++ lstrip(Acc))
+    end;
+replace_edown_p([H|T], Acc) ->
+    replace_edown_p(T, [H|Acc]);
+replace_edown_p([], Acc) ->
+    lists:reverse(Acc).
+
 %% attr(#xmlAttribute{name = N, value = V}) ->
 %%     "(" ++ atom_to_list(N) ++ "=" ++ [a_val(V)] ++ ")".