|;
+
+  my @parts = map {
+    if (substr($_, 0, 1) eq '<') {
+      s{ +}{}g;
+      if ($_ eq '') {
+        $in_p--;
+        $in_p == 0 ? '' : '';
+
+      } elsif ($_ eq '') {
+        $in_p++;
+        $in_p == 1 ? $p_start_tag : '';
+
+      } elsif ($_ eq '
') {
+        $self->{used_list_styles}->{itemize}->{$self->{current_text_style}}   = 1;
+        $html_replace{'- '}                                                 = $ul_li_start_tag;
+        $ul_start_tag;
+
+      } elsif ($_ eq '
') {
+        $self->{used_list_styles}->{enumerate}->{$self->{current_text_style}} = 1;
+        $html_replace{'- '}                                                 = $ol_li_start_tag;
+        $ol_start_tag;
+
+      } else {
+        $html_replace{$_} || '';
+      }
+
+    } else {
+      $::locale->quote_special_chars('Template/OpenDocument', HTML::Entities::decode_entities($_));
+    }
+  } split(m{(<.*?>)}x, $content);
+
+  my $out  = join('', $prefix, @parts, $suffix);
+
+  # $::lxdebug->dump(0, "prefix parts suffix", [ $prefix, join('', @parts), $suffix ]);
+
+  return $out;
+}
+
+my %formatters = (
+  html => \&_format_html,
+  text => \&_format_text,
+);
+
 sub new {
   my $type = shift;
 
   my $self = $type->SUPER::new(@_);
 
-  $self->{"rnd"}   = int(rand(1000000));
-  $self->{"iconv"} = SL::Iconv->new($::lx_office_conf{system}->{dbcharset}, "UTF-8");
-
   $self->set_tag_style('<%', '%>');
   $self->{quot_re} = '"';
 
@@ -102,9 +229,13 @@ sub parse_block {
 
   while ($contents ne "") {
     if (substr($contents, 0, 1) eq "<") {
-      $contents =~ m|^<[^>]+>|;
-      my $tag = $&;
-      substr($contents, 0, length($&)) = "";
+      $contents =~ m|^(<[^>]+>)|;
+      my $tag = $1;
+      substr($contents, 0, length($1)) = "";
+
+      $self->{current_text_style} = $1 if $tag =~ m|text:style-name\s*=\s*"([^"]+)"|;
+
+      push @{ $self->{tag_stack} }, $tag;
 
       if ($tag =~ m|]*>)|;
@@ -114,10 +245,10 @@ sub parse_block {
         if ($table_row =~ m|\<\%foreachrow\s+(.*?)\%\>|) {
           my $var = $1;
 
-          $contents =~ m|\<\%foreachrow\s+.*?\%\>|;
-          substr($contents, length($`), length($&)) = "";
+          $contents =~ m|^(.*?)(\<\%foreachrow\s+.*?\%\>)|;
+          substr($contents, length($1), length($2)) = "";
 
-          ($table_row, $contents) = $self->find_end($contents, length($`));
+          ($table_row, $contents) = $self->find_end($contents, length($1));
           if (!$table_row) {
             $self->{"error"} = "Unclosed <\%foreachrow\%>." unless ($self->{"error"});
             $main::lxdebug->leave_sub();
@@ -128,7 +259,7 @@ sub parse_block {
           $table_row .=  $1;
           $end_tag    =  $2;
 
-          substr $contents, 0, length($&), '';
+          substr $contents, 0, length($1) + length($2), '';
 
           my $new_text = $self->parse_foreach($var, $table_row, $tag, $end_tag, @indices);
           if (!defined($new_text)) {
@@ -151,9 +282,14 @@ sub parse_block {
         $new_contents .= $tag;
       }
 
+      if ($tag =~ m{^ | />$}x) {
+        # $::lxdebug->message(0, "popping top tag is $tag top " . $self->{tag_stack}->[-1]);
+        pop @{ $self->{tag_stack} };
+      }
+
     } else {
-      $contents =~ /^[^<]+/;
-      my $text = $&;
+      $contents =~ /^([^<]+)/;
+      my $text = $1;
 
       my $pos_if = index($text, '<%if');
       my $pos_foreach = index($text, '<%foreach');
@@ -168,15 +304,15 @@ sub parse_block {
         $new_contents .= $self->substitute_vars(substr($contents, 0, $pos_foreach), @indices);
         substr($contents, 0, $pos_foreach) = "";
 
-        if ($contents !~ m|^\<\%foreach (.*?)\%\>|) {
+        if ($contents !~ m|^(\<\%foreach (.*?)\%\>)|) {
           $self->{"error"} = "Malformed <\%foreach\%>.";
           $main::lxdebug->leave_sub();
           return undef;
         }
 
-        my $var = $1;
+        my $var = $2;
 
-        substr($contents, 0, length($&)) = "";
+        substr($contents, 0, length($1)) = "";
 
         my $block;
         ($block, $contents) = $self->find_end($contents);
@@ -236,29 +372,11 @@ sub parse {
     return 0;
   }
 
-  my $rnd = $self->{"rnd"};
-  my $new_styles = qq|
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-|;
-
-  $contents =~ s||${new_styles}|;
-  $contents =~ s|[\n\r]||gm;
+  $self->{current_text_style} =  '';
+  $self->{used_list_styles}   =  {
+    itemize                   => {},
+    enumerate                 => {},
+  };
 
   my $new_contents;
   if ($self->{use_template_toolkit}) {
@@ -266,6 +384,7 @@ sub parse {
 
     $::form->init_template->process(\$contents, $additional_params, \$new_contents) || die $::form->template->error;
   } else {
+    $self->{tag_stack} = [];
     $new_contents = $self->parse_block($contents);
   }
   if (!defined($new_contents)) {
@@ -273,6 +392,20 @@ sub parse {
     return 0;
   }
 
+  my $new_styles = SL::Template::OpenDocument::Styles->get_style('text_basic');
+
+  foreach my $type (qw(itemize enumerate)) {
+    foreach my $parent (sort { $a cmp $b } keys %{ $self->{used_list_styles}->{$type} }) {
+      $new_styles .= SL::Template::OpenDocument::Styles->get_style('text_list_item', TYPE => $type, PARENT => $parent)
+                   .  SL::Template::OpenDocument::Styles->get_style("list_${type}",  TYPE => $type, PARENT => $parent);
+    }
+  }
+
+  # $::lxdebug->dump(0, "new_Styles", $new_styles);
+
+  $new_contents =~ s||${new_styles}|;
+  $new_contents =~ s|[\n\r]||gm;
+
 #   $new_contents =~ s|>|>\n|g;
 
   $zip->contents("content.xml", Encode::encode('utf-8-strict', $new_contents));
@@ -573,25 +706,14 @@ sub convert_to_pdf {
 }
 
 sub format_string {
-  my ($self, $variable) = @_;
-  my $form = $self->{"form"};
-  my $iconv = $self->{"iconv"};
-
-  $variable = $main::locale->quote_special_chars('Template/OpenDocument', $variable);
+  my ($self, $content, $variable) = @_;
 
-  # Allow some HTML markup to be converted into the output format's
-  # corresponding markup code, e.g. bold or italic.
-  my $rnd = $self->{"rnd"};
-  my %markup_replace = ("b" => "BOLD", "i" => "ITALIC", "s" => "STRIKETHROUGH",
-                        "u" => "UNDERLINE", "sup" => "SUPER", "sub" => "SUB");
-
-  foreach my $key (keys(%markup_replace)) {
-    my $value = $markup_replace{$key};
-    $variable =~ s|\<${key}\>||gi; #"
-    $variable =~ s|\</${key}\>||gi;
-  }
+  my $formatter =
+       $formatters{ $self->{variable_content_types}->{$variable} }
+    // $formatters{ $self->{default_content_type} }
+    // $formatters{ text };
 
-  return $iconv->convert($variable);
+  return $formatter->($self, $content, variable => $variable);
 }
 
 sub get_mime_type() {