+my %text_markup_replace = (
+  b   => "BOLD",
+  i   => "ITALIC",
+  s   => "STRIKETHROUGH",
+  u   => "UNDERLINE",
+  sup => "SUPER",
+  sub => "SUB",
+);
+
+sub _format_text {
+  my ($self, $content, %params) = @_;
+
+  $content = $::locale->quote_special_chars('Template/OpenDocument', $content);
+
+  # Allow some HTML markup to be converted into the output format's
+  # corresponding markup code, e.g. bold or italic.
+  foreach my $key (keys(%text_markup_replace)) {
+    my $value = $text_markup_replace{$key};
+    $content =~ s|\<${key}\>|<text:span text:style-name=\"TKIVITENDO${value}\">|gi; #"
+    $content =~ s|\</${key}\>|</text:span>|gi;
+  }
+
+  return $content;
+}
+
+my %html_replace = (
+  '</ul>'     => '</text:list>',
+  '</ol>'     => '</text:list>',
+  '</li>'     => '</text:p></text:list-item>',
+  '<b>'       => '<text:span text:style-name="TKIVITENDOBOLD">',
+  '</b>'      => '</text:span>',
+  '<strong>'  => '<text:span text:style-name="TKIVITENDOBOLD">',
+  '</strong>' => '</text:span>',
+  '<i>'       => '<text:span text:style-name="TKIVITENDOITALIC">',
+  '</i>'      => '</text:span>',
+  '<em>'      => '<text:span text:style-name="TKIVITENDOITALIC">',
+  '</em>'     => '</text:span>',
+  '<u>'       => '<text:span text:style-name="TKIVITENDOUNDERLINE">',
+  '</u>'      => '</text:span>',
+  '<s>'       => '<text:span text:style-name="TKIVITENDOSTRIKETHROUGH">',
+  '</s>'      => '</text:span>',
+  '<sub>'     => '<text:span text:style-name="TKIVITENDOSUB">',
+  '</sub>'    => '</text:span>',
+  '<sup>'     => '<text:span text:style-name="TKIVITENDOSUPER">',
+  '</sup>'    => '</text:span>',
+  '<br/>'     => '<text:line-break/>',
+  '<br>'      => '<text:line-break/>',
+);
+
+sub _format_html {
+  my ($self, $content, %params) = @_;
+
+  my $in_p        = 0;
+  my $p_start_tag = qq|<text:p text:style-name="@{[ $self->{current_text_style} ]}">|;
+  my $prefix      = '';
+  my $suffix      = '';
+
+  my (@tags_to_open, @tags_to_close);
+  for (my $idx = scalar(@{ $self->{tag_stack} }) - 1; $idx >= 0; --$idx) {
+    my $tag = $self->{tag_stack}->[$idx];
+
+    next if $tag =~ m{/>$};
+    last if $tag =~ m{^<table};
+
+    if ($tag =~ m{^<text:p}) {
+      $in_p        = 1;
+      $p_start_tag = $tag;
+      last;
+
+    } else {
+      $suffix  =  "${tag}${suffix}";
+      $tag     =~ s{ .*>}{>};
+      $prefix .=  '</' . substr($tag, 1);
+    }
+  }
+
+  $content            =~ s{ ^<p> | </p>$ }{}gx if $in_p;
+  $content            =~ s{ \r+ }{}gx;
+  $content            =~ s{ \n+ }{ }gx;
+  $content            =~ s{ (?:\ |\s)+ }{ }gx;
+
+  my $ul_start_tag    = qq|<text:list xml:id="list@{[ int rand(9999999999999999) ]}" text:style-name="LKIVITENDOitemize@{[ $self->{current_text_style} ]}">|;
+  my $ol_start_tag    = qq|<text:list xml:id="list@{[ int rand(9999999999999999) ]}" text:style-name="LKIVITENDOenumerate@{[ $self->{current_text_style} ]}">|;
+  my $ul_li_start_tag = qq|<text:list-item><text:p text:style-name="PKIVITENDOitemize@{[ $self->{current_text_style} ]}">|;
+  my $ol_li_start_tag = qq|<text:list-item><text:p text:style-name="PKIVITENDOenumerate@{[ $self->{current_text_style} ]}">|;
+
+  my @parts = map {
+    if (substr($_, 0, 1) eq '<') {
+      s{ +}{}g;
+      if ($_ eq '</p>') {
+        $in_p--;
+        $in_p == 0 ? '</text:p>' : '';
+
+      } elsif ($_ eq '<p>') {
+        $in_p++;
+        $in_p == 1 ? $p_start_tag : '';
+
+      } elsif ($_ eq '<ul>') {
+        $self->{used_list_styles}->{itemize}->{$self->{current_text_style}}   = 1;
+        $html_replace{'<li>'}                                                 = $ul_li_start_tag;
+        $ul_start_tag;
+
+      } elsif ($_ eq '<ol>') {
+        $self->{used_list_styles}->{enumerate}->{$self->{current_text_style}} = 1;
+        $html_replace{'<li>'}                                                 = $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,
+);
+