]*$id>\Q$del\E)([^$esc_del\n]*?)(\Q$del\E|$comment_mark(\d+)\n?)/
 	    $contents=$2;
@@ -3178,7 +3189,7 @@
     
     if ($style_names) { $envS = "$style_names" }
     elsif (($envS =~ /^pre$/)&&
-	(/^\\begin.*preform($O|$OP)\d+($C|$CP)$verbatim_mark(\w*[vV]erbatim)(\*?)/))
+	(/^\\begin.*preform($O|$OP)\d+($C|$CP)$verbatim_mark(\w*[vV]erbatim|lstlisting)(\*?)/))
 	    { $envS = $3.($4 ? 'star' : '') };
     $env_style{$envS} = " " unless (($style_names)||($env_style{$envS}));
     $env_id = " ID=\"$env_id\"".(($envS) ? " CLASS=\"$envS\"" : '');
@@ -4165,7 +4176,15 @@
 	  $flip, $aalias, $trans, $exscale, $alt, $exstr);
 
     my ($lwidth, $val) = (0, '');
-    my ($custom_size,$color_depth,$height,$width,$croparg);
+    my ($custom_size,$color_depth,$color_quant,$height,$width,$croparg);
+
+    # The combination -use_pdftex -use_dvipng uses gs driver png16m
+    # which can be incompatible with gif format (>256 colors).
+    $color_depth = $color_quant = '';
+    if (($USE_PDFTEX || $USE_LUATEX) && $USE_DVIPNG && $IMAGE_TYPE =~ /gif/) {
+        $color_depth = "-depth 8 ";
+        $color_quant = "|$PPMQUANT -floyd 256";
+    }
 
     print STDOUT "\nextracting $name as $page_num\n" if ($VERBOSITY > 1);
     # $global_num identifies this image in the original source file
@@ -4214,9 +4233,9 @@
             print STRUT "P5\n1 3\n255\nEEE\n"; close STRUT}
         #L2hos->syswait("$PNGTOPNM $p.png|$PNMCROP -sides|tee $p.ppm|$PNMCUT -top $millimeter |$PNMCROP -sides|tee $p-.ppm|$PPMTO{$IMAGE_TYPE} --quiet $transparent >img$new_num.$IMAGE_TYPE;");
         if ($name =~ /figure|table/) {
-	  L2hos->syswait("$PNGTOPNM $p.png|$PPMTO --quiet $transparent >img$new_num.$IMAGE_TYPE;");
+	  L2hos->syswait("$PNGTOPNM $p.png $color_quant|$PPMTO --quiet $transparent >img$new_num.$IMAGE_TYPE;");
 	} else {
-	  L2hos->syswait("$PNGTOPNM $p.png|$PNMCROP -sides|tee $p.ppm|$PNMCUT -top $millimeter |$PNMCROP -left -right|tee $p-.ppm|$PPMTO --quiet $transparent >img$new_num.$IMAGE_TYPE;");
+	  L2hos->syswait("$PNGTOPNM $p.png|$PNMCROP -sides|tee $p.ppm|$PNMCUT -top $millimeter |$PNMCROP -left -right|tee $p-.ppm $color_quant|$PPMTO --quiet $transparent >img$new_num.$IMAGE_TYPE;");
 	}
         open PPM,"<$p.ppm"; read(PPM,$_,30);close PPM;
         m/^(\d+) (\d+)$/m;
@@ -4328,15 +4347,18 @@
 		    $under = "d" if (($name =~/inline|indisplay/)&&($depth{$name}));
 		}
 		print "\nIMAGE: $name  scaled by $scale \n" if ($VERBOSITY > 2);
+		$color_depth = ''
+		    if (($ANTI_ALIAS_TEXT || $aalias) && $aalias !~ /no/);
 		(L2hos->syswait( "$PSTOIMG -type $IMAGE_TYPE "
 		. ($DEBUG ? '-debug ' : '-quiet ' )
 		. ($TMPDIR ? "-tmp $TMPDIR " : '' )
 		. (($DISCARD_PS)? "-discard " : '' )
 		. (($INTERLACE)? "-interlace " : '' )
-		. ((($ANTI_ALIAS_TEXT||($aalias))&&($aalias !=~/no/))? 
+		. ((($ANTI_ALIAS_TEXT||($aalias))&&($aalias !~ /no/))? 
 		    "-antialias -depth 1 " :'')
 		. (($custom_size)? "-geometry $custom_size " : '' )
                 . $croparg
+		. ($color_depth || '')
 		. (($scale != 1)? "-scale $scale " : '' )
 		. ((($exscale)&&($exscale != 1)&&
 		    !($ANTI_ALIAS_TEXT &&($LATEX_COLOR)))? 
@@ -4362,6 +4384,8 @@
 		    $under = "d" if (($name =~/inline|indisplay/)&&($depth{$name}));
 		} elsif ($mathscale) { $scale = $mathscale; }
 
+		$color_depth = ''
+		    if (($ANTI_ALIAS_TEXT || $aalias) && $aalias !~ /no/);
 		(L2hos->syswait("$PSTOIMG -type $IMAGE_TYPE "
 		. ($DEBUG ? '-debug ' : '-quiet ' )
 		. ($TMPDIR ? "-tmp $TMPDIR " : '' )
@@ -4375,6 +4399,7 @@
 		. (($scale ne 1) ? "-scale $scale " : '' )
 		. (($custom_size) ? "-geometry $custom_size " : '' )
                 . $croparg
+		. ($color_depth || '')
 #		.  (($name =~ /(equation|eqnarray)/) ? "-rightjustify $lwidth " : '')
 #		.  (($name =~ /displaymath/) ? "-center $lwidth " : '')
 		. (($name =~ /inline|indisplay/ && (!($custom_size))&&$depth{$name}!= 0) ?
@@ -7224,6 +7249,74 @@
     . (($length > 500) ? "\\setlength{\\textheight}{${length}pt}\n" : '')
 }
 
+# Return numeric width and height of text area depending on paper format
+# Analogous to adjust_textwidth, used for \includegraphics
+sub get_text_size {
+    local($_) = @_;
+    local($width,$length) = ('','');
+    if (/a4/) {$width = 595; $length= 842; }
+    elsif (/letter/) {$width = 612; $length= 792; }
+    elsif (/legal/) {$width = 612; $length= 1008; }
+    elsif (/note/) {$width = 540; $length= 720; }
+    elsif (/b5/) {$width = 501; $length= 709; }
+    elsif (/a5/) {$width = 421; $length= 595; }
+    elsif (/a6/) {$width = 297; $length= 421; }
+    elsif (/a7/) {$width = 210; $length= 297; }
+    elsif (/a8/) {$width = 148; $length= 210; }
+    elsif (/a9/) {$width = 105; $length= 148; }
+    elsif (/a10/) {$width = 74; $length= 105; }
+    elsif (/b4/) {$width = 709; $length= 1002; }
+    elsif (/a3/) {$width = 842; $length= 1190; }
+    elsif (/b3/) {$width = 1002; $length= 1418; }
+    elsif (/a2/) {$width = 1190; $length= 1684; }
+    elsif (/b2/) {$width = 1418; $length= 2004; }
+    elsif (/a1/) {$width = 1684; $length= 2380; }
+    elsif (/b1/) {$width = 2004; $length= 2836; }
+    elsif (/a0/) {$width = 2380; $length= 3368; }
+    elsif (/b0/) {$width = 2836; $length= 4013; }
+    else {
+	&write_warnings("\nPAPERSIZE: $_ unknown, using a5.");
+	$width = 421; $length= 595;
+    }
+    if ($width > 500) { $width = $width - 144; $length = $length - 288; }
+    elsif ($width > 250) { $width = $width - 72; $length = $length - 144; }
+    elsif ($width > 125) { $width = $width - 36; $length = $length - 72; }
+
+    # Evtl adjust according to wrapfigure dimensions
+    if ($wrapfigure_width) {	# defined inside wrapfigure only
+      my($pxs,$len) = &convert_length($wrapfigure_width, 1);
+      if ($pxs =~ s/%$//) {	# scale width and length by percent
+	$width  *= $pxs/100;
+	$length *= $pxs/100;
+      } else { # revert scaling, set explicit width, recalculate length
+	$pxs /= $FIGURE_SCALE_FACTOR*0.8;
+	$length *= $pxs/$width;
+	$width = $pxs;
+      }
+    }
+
+    # Evtl adjust according to minipage dimensions (possibly nested)
+    if ($MINIPAGE) {
+      my($i, $mw);
+      for ($i=0; $i<$MINIPAGE; $i++) {
+	$mw = $minipage_width[$i];
+	if ($mw =~ s/%$//) {	# scale width and length by percent
+	  $width  *= $mw/100;
+	  $length *= $mw/100;
+	} else { # revert scaling, set explicit width, recalculate length
+	  $mw /= $MATH_SCALE_FACTOR*$FIGURE_SCALE_FACTOR*0.8;
+	  $length *= $mw/$width;
+	  $width = $mw;
+	}
+      }
+    }
+
+    # Round results and return
+    $width  = sprintf("%.0f", $width);
+    $length = sprintf("%.0f", $length);
+    ($width, $length);
+}
+
 # Given the depth of the current sectioning declaration and the current
 # section numbers it returns the new section numbers.
 # It increments the $depth-ieth element of the @curr_sec_id list and
@@ -7366,7 +7459,7 @@
             print STYLESHEET "SPAN.$env\t\t{ $style }\n";
         } elsif ($env =~ /\./) {
             print STYLESHEET "$env\t\t{ $style }\n";
-        } elsif ($env =~ /^(preform|\w*[Vv]erbatim(star)?)$/) {
+        } elsif ($env =~ /^(preform|lstlisting|\w*[Vv]erbatim(star)?)$/) {
             print STYLESHEET "PRE.$env\t\t{ $style }\n";
         } elsif ($env =~ /figure|table|tabular|equation|$array_env_rx/) {
             print STYLESHEET "TABLE.$env\t\t{ $style }\n";
@@ -7878,7 +7971,7 @@
     if (defined &replace_verbatim_hook) {&replace_verbatim_hook;}
     else {&replace_verbatim_marks if /$verbatim_mark/;}
     if (defined &replace_verb_hook) {&replace_verb_hook;}
-    else {&replace_verb_marks if /$verb_mark|$verbstar_mark/;}
+    else {&replace_verb_marks if /$verb_mark|$verbstar_mark|$verblst_mark/;}
     s/;SPMdollar;/\$/g; s/;SPMtilde;/\~/g; s/;SPMpct;/\%/g;
     s/;SPM/\&/go;
     s/$percent_mark/%/go;
@@ -8785,6 +8878,8 @@
     s!$verbatim_mark(\w*[vV]erbatim\*?|tex2html_code)(\d+)#\n?!$tmp=$verbatim{$2};
 	$tmp.(($tmp =~/\n\s*$/s)? '':"\n")!eg;
 #	"\n".$tmp.(($tmp =~/\n\s*$/s)? '':"\n")!eg;
+    s/(]*>)?$verbatim_mark(lstlisting|lstset|lststyle|lstfile)(\d+)#(<\/PRE>)?\n?/
+        &process_lstlisting($1, $4, $2, $verbatim{$3})/eg;
 #    s/$verbatim_mark(rawhtml)(\d+)#/$verbatim{$2}/eg; # Raw HTML
     s/$verbatim_mark(imagesonly)(\d+)#//eg; # imagesonly is *not* replaced
     # Raw HTML, but replacements may have protected characters
@@ -8793,6 +8888,8 @@
     s/$unfinished_mark$keepcomments_rx(\d+)#/$verbatim{$2}/ego; # Raw TeX
 }
 
+# Actual lstlisting engine, except initialization, moved to styles/listings.perl
+
 # TeX's special characters may have been escaped with a '\'; remove it.
 sub unprotect_raw_html {
     local($raw) = @_;
@@ -8813,16 +8910,20 @@
 #    s/$verbatim_mark(verbatim\*?)(\d+)#//go;
     s/$verbatim_mark(\w*[Vv]erbatim\w*\*?)(\d+)#//go;
     s/$verbatim_mark(rawhtml|imagesonly)(\d+)#//go;
+    s/$verbatim_mark(lstlisting)(\d+)#//go;
     s/$verbatim_mark$keepcomments_rx(\d+)#//go;
     s/$unfinished_mark$keepcomments_rx(\d+)#//go;
 }
 
 sub replace_verb_marks {
     # Modifies $_
-    s/(?:$verb_mark|$verbstar_mark)(\d+)$verb_mark/
-	$code = $verb{$1};
+    s/($verb_mark|$verbstar_mark|$verblst_mark)(\d+)$verb_mark/
+	$code = $verb{$2};
 	$code = &replace_comments($code) if ($code =~ m:$comment_mark:);
-	"$code<\/code>"/ego;
+	if ($1 eq $verblst_mark)
+	{$code=&process_lstinline($verb_lstopt{$2},$code);}
+	else {$code="$code<\/code>";}
+	$code/eg;
 }
 
 sub replace_comments{
@@ -8834,7 +8935,7 @@
 
 sub remove_verb_marks {
     # Modifies $_
-    s/($verb_mark|$verbstar_mark)(\d+)$verb_mark//go;
+    s/($verb_mark|$verbstar_mark|$verblst_mark)(\d+)$verb_mark//go;
 }
 
 # This is used by revert_to_raw_tex
@@ -8842,6 +8943,7 @@
     # Modifies $_
 #    s/$verbatim_mark(verbatim)(\d+)#/\\begin{verbatim}$verbatim{$2}\\end{verbatim}\n/go;
     s/$verbatim_mark(\w*[Vv]erbatim)(\d+)#/\\begin{$1}\n$verbatim{$2}\\end{$1}\n/go;
+    s/$verbatim_mark(lstlisting)(\d+)#/\\begin{lstlisting}\n$verbatim{$2}\\end{lstlisting}\n/go;
     s/$verbatim_mark(rawhtml)(\d+)#/\\begin{rawhtml}\n$verbatim{$2}\\end{rawhtml}\n/go;
     s/$verbatim_mark(imagesonly|tex2html_code)(\d+)#\n?/$verbatim{$2}/go;
     s/$verbatim_mark$image_env_rx(\d+)#/\\begin{$1}\n$verbatim{$2}\\end{$1}\n/go;
@@ -8852,6 +8954,7 @@
     # Modifies $_
     s/$verbstar_mark(\d+)$verb_mark/\\verb*$verb_delim{$1}$verb{$1}$verb_delim{$1}/go;
     s/$verb_mark(\d+)$verb_mark/\\verb$verb_delim{$1}$verb{$1}$verb_delim{$1}/go;
+    s/$verblst_mark(\d+)$verb_mark/\\lstinline$verb_lstopt{$1}$verb_delim{$1}$verb{$1}$verb_delim{$1}/go;
 }
 
 sub replace_cross_ref_marks {
@@ -8886,7 +8989,7 @@
 #RRM: this simply absorbs the name from the invisible anchor following, 
 #     when the anchor itself is not already named.
 sub extend_ref {
-    if ($ref_label !=~ /NAME=/) { $label .= "\"\n".$name  }
+    if ($ref_label !~ /NAME=/) { $label .= "\"\n".$name  }
 }
 
 sub remove_cross_ref_marks {
@@ -9892,7 +9995,7 @@
     local($pre_bit);
     if ($lastbit =~/>([^>]*)$/) { 
 	$word = $1; $pre_bit = $`.'>';
-	if ($pre_bit =~ /($verb_mark|$verbstar_mark)$/) {
+	if ($pre_bit =~ /($verb_mark|$verbstar_mark|$verblst_mark)$/) {
 	    $word = $lastbit;
 	} elsif ($pre_bit =~ /<\w+_mark>$/) {
 	    $word = $& . $word;
@@ -11296,6 +11399,8 @@
 # not produce the same bullets as in the dvi output.
 sub do_env_itemize {
     local($_) = @_;
+    local($dum1, $dum2);
+    ($dum,$dum2) = &get_next_optional_argument; # discard optional argument
     $itemize_level++;
     #RRM - catch nested lists
     &protect_useritems($_);
@@ -11329,6 +11434,8 @@
 }
 sub do_env_enumerate {
     local($_) = @_;
+    local($dum1, $dum2);
+    ($dum,$dum2) = &get_next_optional_argument; # discard optional argument
 # Reiner Miericke provided the main code; integrated by RRM: 14/1/97
 # works currently only with 'enumerate' and derived environments
 # explicit styled labels are computed for each \item
@@ -11582,6 +11689,8 @@
 
 sub do_env_description {
     local($_, $compact, $bullet) = @_;
+    local($dum1, $dum2);
+    ($dum,$dum2) = &get_next_optional_argument; # discard optional argument
     #RRM - catch nested lists
     &protect_useritems($_);
     $_ = &translate_environments($_) unless ($bullet);
@@ -11947,7 +12056,8 @@
     $width = &missing_braces unless (
     	(s/$next_pair_pr_rx/$width=$2;''/e)
     	||(s/$next_pair_rx/$width=$2;''/e));
-    local($pxs,$len) = &convert_length($width,$MATH_SCALE_FACTOR) if $width;
+    local($pxs,$len);
+    ($pxs,$len) = &convert_length($width,$MATH_SCALE_FACTOR) if $width;
     $width = " WIDTH=\"$pxs\"";
     
     local ( %mpfootnotes, $mpfootnotes ) unless ($MINIPAGE);
@@ -11955,6 +12065,7 @@
     $global{'mpfootnote'} = 0 unless ($MINIPAGE);
     $MINIPAGE++;
     print "\n *** doing minipage *** " if ($VERBOSITY > 1);
+    $minipage_width[$MINIPAGE-1] = $pxs if ($MINIPAGE);
     local($open_tags_R) = [ @$open_tags_R ];
     local($close_tags,$reopens) = &close_all_tags();
     local(@save_open_tags) = @$open_tags_R;
@@ -12905,9 +13016,10 @@
 sub do_cmd_endgraf { join('',"
", $_[0]); }
 
 sub do_cmd_space { join(''," ",$_[0]); }
-sub do_cmd_enspace { join('',"\ ",$_[0]); }
-sub do_cmd_quad { join('',"\ "x4,$_[0]); }
-sub do_cmd_qquad { join('',"\ "x8,$_[0]); }
+sub do_cmd_enspace { join('',";SPMnbsp;",$_[0]); }
+sub do_cmd_senspace { join('',";SPMnbsp;",$_[0]); }
+sub do_cmd_quad { join('',";SPMnbsp;"x4,$_[0]); }
+sub do_cmd_qquad { join('',";SPMnbsp;"x8,$_[0]); }
 
 sub do_cmd_par {
     local ($_) = @_;
@@ -15774,6 +15886,7 @@
     $unfinished_mark = '';
     $verb_mark = '';
     $verbstar_mark = '';
+    $verblst_mark = '';
     $image_mark = '';
     $mydb_mark =  '';
     $percent_mark = '';
@@ -16056,7 +16169,7 @@
 		  , '~', 'tilde', '.', 'dot', '=', 'bar'
 		  , '{', 'lbrace' , '}', 'rbrace', '|', 'Vert'
 		  , '#', 'esc_hash', '$', 'esc_dollar'
-          , ',', 'enspace'      
+          , ',', 'enspace', ';', 'senspace'
       );
 
     %text_accent = (  'cedil','c', 'bdot','d', 'b','b' , 'tilde','~'
@@ -16169,7 +16282,9 @@
 }
 
 
-    %unitscale = ("in",72,"pt",72.27/72,"pc",12,"mm",72/25.4,"cm",72/2.54
+    %unitscale = ("in",72,"pt",72/72.27,"pc",12*72/72.27
+                  ,"mm",72/25.4,"cm",72/2.54
+    	          ,"ex",72/25.4*5,"em",72/25.4*5
 		  ,"\\hsize",100,"\\vsize",100
 		  ,"\\textwidth",100,"\\textheight",100
 		  ,"\\pagewidth",100,"\\linewidth",100
@@ -16181,17 +16296,49 @@
     my ($this,$scale) = @_;
     $scale = 1 unless $scale;
     my ($pxs,$len,$full);
-    if ( $this =~ /([\d.]*)\s*(in|pt|pc|mm|cm|\\[hv]size|\\\w+(width|height))?/ ) {
-	$len = ($1 ? $1 : 1); $full = $2;
-	if ($full &&($full =~ /\\([hv]size|\w+(width|height))/)) { $scale = 1;};
+    if ( $this =~ /([\d.]*)\s*(in|pt|pc|mm|cm|ex|em|\\[hv]size|\\\w+(width|height))?/ ) {
+	$len = (($1 ne '') ? $1 : 1); $full = $2;
+	if ($full &&($full =~ /\\([hv]size|\w+(width|height))/)) { $scale = 1;}
+	else { $scale *= $FIGURE_SCALE_FACTOR*0.8; }
+	if ($full eq 'em' || $full eq 'ex') {
+	    $unitscale{$full} = $LATEX_FONT_SIZE;
+	    $unitscale{$full} = 12 if ($unitscale{$full} < 1);
+	}
 	$pxs = (($full) ? int($len * $unitscale{$full}*$scale + 0.5)
 		 : int($len*$scale + .5) );
 	if ( $full =~ /\\([hv]size|\w+(width|height))/) { $pxs .= '%';};
     };
     ($pxs,$len);
 }
- 
 
+    # Initializations block for verbatim-like lstlisting environment
+    # Actual lstlisting engine moved to styles/listings.perl
+    # Currently default option values for lstlisting environment
+    %lstset_current = (
+      'numbers'         => 'none',
+      'stepnumber'      => 1,
+      'numberfirstline' => 'false',
+      'numbersep'       => '10pt',
+      'firstnumber'     => 'auto',
+      'name'            => '',
+      'title'           => '',
+      'caption'         => '',
+      'captionpos'      => 't',
+      'basicstyle'      => '',
+      'style'           => '',
+      'inputpath'       => '',
+      'frame'           => 'none',
+      'framesep'        => '3pt',
+      'framerule'       => '0.4pt',
+      'backgroundcolor' => '',
+      'rulecolor'       => '' );
+    # Option sets defined by lstdefinestyle
+    %lstset_style = ();
+    # Placeholder for \lstname
+    $lst_name = '';
+    # For line counters
+    $lst_last_counter = 1;
+    %lst_auto_counter = ();
 
 
     # Inclusion in this list will cause a command or an environment to be ignored.
@@ -16666,6 +16813,7 @@
 
     $opt_arg_rx = "\\s*\\[([^\\]]*)\\]\\s*";	# Cannot handle nested []s!
     $optional_arg_rx = "^\\s*\\[([^]]*)\\]";	# Cannot handle nested []s!
+    $verb_braces_rx = "\\s*\\{([^}]*)\\}\\s*";	# Cannot handle nested {}s!
 
     $block_close_rx = "^<\\/(DIV|P|BLOCKQUOTE)>\$";
     $all_close_rx = "^<\\/(BODY|PRE|OL|UL|DL|FORM|ADDRESS)>\$";
@@ -16822,7 +16970,7 @@
 
     # Matches environments that should not be touched during the translation
 #   $verbatim_env_rx = "\\s*{(verbatim|rawhtml|LVerbatim)[*]?}";
-    $verbatim_env_rx = "\\s*(\\w*[Vv]erbatim|rawhtml|imagesonly|tex2html_code)[*]?";
+    $verbatim_env_rx = "\\s*(\\w*[Vv]erbatim|lstlisting|rawhtml|imagesonly|tex2html_code)[*]?";
     $image_env_rx = "\\s*(picture|xy|diagram)[*]?";
     $keepcomments_rx = "\\s*(picture|makeimage|xy|diagram)[*]?";
 
diff -Naur latex2html-2018.2.orig/styles/graphics-support.perl latex2html-2018.2/styles/graphics-support.perl
--- latex2html-2018.2.orig/styles/graphics-support.perl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/styles/graphics-support.perl	2020-11-10 21:32:16.310070346 +0700
@@ -255,14 +255,31 @@
 
 # convert_length rounds up to 1 & uses %. Not quite right for here.
 #  [convert_length(@_)]->[0];
-%BP_conversions=(pt=>72/72.27, pc=>12/72.27, in=>72, bp=>1,
+%BP_conversions=(pt=>72/72.27, pc=>12*(72/72.27), in=>72, bp=>1,
 		 cm=>72/2.54, mm=>72/25.4, dd=>(72/72.27)*(1238/1157),
-		 cc=>12*(72/72.27)*(1238/1157),sp=>72/72.27/65536);
+		 cc=>12*(72/72.27)*(1238/1157),sp=>(72/72.27)/65536);
 sub to_bp { 
   my($x)=@_;
-  $x =~ /^\s*([+-]?[\d\.]+)(\w*)\s*$/;
-  my($v,$u)=($1,$2);
-  $v*($u ? $BP_conversions{$u} : 1); }
+  my($v,$u);
+  $x =~ /^\s*(([+-]?[\d\.]+)?)\s*(\w*|\\\w+(width|height))\s*$/;
+  $v = ($1 ? $1 : 1);
+  $u = $3;
+  # Induce using native image dimensions if given size totally unrecognized
+  return '' if($1 eq '' && $3 eq '');
+  if ($u =~ /\\\w+(width|height)/) {
+    my($w,$h);
+    if ($PAPERSIZE) { ($w,$h) = &get_text_size($PAPERSIZE); }
+    else { ($w,$h) = &get_text_size("a5"); }
+    if ($u =~ /height/) { $u = $h; }
+    else { $u = $w; }
+  }
+  elsif ($u eq 'em' || $u eq 'ex') {
+    $u = $LATEX_FONT_SIZE;
+    $u = 12 if ($u < 1);
+  }
+  else { $u = $u ? $BP_conversions{$u} : 1; }
+  $u *= $FIGURE_SCALE_FACTOR*0.8;	# just an extra empirical coefficient
+  $v*$u; }
 
 sub scaled_image_size {
   my($imagew,$imageh, $reqw,$reqh,$scale,$keepaspect)=@_;
diff -Naur latex2html-2018.2.orig/styles/html.perl latex2html-2018.2/styles/html.perl
--- latex2html-2018.2.orig/styles/html.perl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/styles/html.perl	2020-11-10 21:33:29.800071426 +0700
@@ -8,6 +8,17 @@
 # Extension to LaTeX2HTML to translate hypermedia extensions
 # to LaTeX defined in html.sty to equivalent HTML commands.
 #
+# WARNING: \hyperref is also declared in hyperref.sty / hyperref.perl.
+# Interfaces of both instances are incompatible.
+# Therefore we declare additional command  \hyperrefhyper
+# whose interface is like that of \hyperref from hyperref.sty.
+#
+# If both hyperref.sty and html.sty are necessary,
+# and \hyperref from hyperref.sty is needed, the following workaround may do:
+#
+# \usepackage{hyperref}
+# \usepackage{html}
+# \renewcommand{\hyperref}[2][]{\hyperrefhyper[#1]{#2}}
 #
 #
 # Modifications (Initials see Changes):
@@ -556,6 +567,8 @@
     &process_cite("external",'');
 }
 
+# Provide the \hyperref command with interface of the html package.
+# Will redefine \hyperref of hyperref package if both are used.
 sub do_cmd_hyperref {
     local($_) = @_;
     local($text,$url,$hypopt);
@@ -594,6 +607,33 @@
     &process_ref($cross_ref_mark,$cross_ref_mark,$text);
 }
 
+# Provide the \hyperrefhyper command
+# emulating \hyperref interface of the hyperref package
+sub do_cmd_hyperrefhyper {
+    local($_) = @_;
+    local($text,$url);
+    local($opt, $dummy) = &get_next_optional_argument;
+    if ($opt) {
+	$text = &missing_braces unless
+	    ((s/$next_pair_pr_rx/$text = $2; ''/eo)
+	    ||(s/$next_pair_rx/$text = $2; ''/eo));
+	local($br_id) = ++$global{'max_id'};
+	$_ = join('', $OP.$br_id.$CP
+		    , $opt 
+		    , $OP.$br_id.$CP , $_ );
+	return (&process_ref($cross_ref_mark,$cross_ref_mark,$text));
+    }
+    $url = &missing_braces unless
+	((s/$next_pair_pr_rx/$url = $2; ''/eo)
+	||(s/$next_pair_rx/$url = $2; ''/eo));
+    s/$next_pair_pr_rx/$url.="\#$2.";''/eo;
+    s/$next_pair_pr_rx/$url.= (($url=~ m|\.$|)? '':'#').$2;''/e;
+    $text = &missing_braces unless
+	((s/$next_pair_pr_rx/$text = $2; ''/eo)
+	||(s/$next_pair_rx/$text = $2; ''/eo));
+    &make_href($url,$text);
+}
+
 sub do_cmd_hypercite {
     local($_) = @_;
     local($text);
diff -Naur latex2html-2018.2.orig/styles/hyperref.perl latex2html-2018.2/styles/hyperref.perl
--- latex2html-2018.2.orig/styles/hyperref.perl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/styles/hyperref.perl	2020-11-10 21:34:37.520072422 +0700
@@ -8,6 +8,17 @@
 # and therefore allows latex2html to process files
 # written for hyperref.sty without modifying them for latex2html
 
+# WARNING: html.sty / html.perl also declares its own instance of \hyperref
+# with an incompatible interface. Additionally declared is \hyperrefhyper
+# whose interface is like that of \hyperref from hyperref.sty.
+#
+# If both hyperref.sty and html.sty are necessary,
+# and \hyperref from hyperref.sty is needed, the following workaround may do:
+#
+# \usepackage{hyperref}
+# \usepackage{html}
+# \renewcommand{\hyperref}[2][]{\hyperrefhyper[#1]{#2}}
+
 package main;
 
 # Suppress option warning messages:
@@ -32,4 +43,45 @@
     "$anchor".$_;
 }
 
+sub do_cmd_hyperref {
+    local($_) = @_;
+    local($opt, $dummy) = &get_next_optional_argument;
+    if ($opt ne '') {
+        return (&process_hyperref($cross_ref_mark,$opt));
+    }
+    local($url, $anchor, $name, $text);
+    $url = &missing_braces unless (
+	(s/$next_pair_pr_rx/$url = $2;''/eo)
+	||(s/$next_pair_rx/$url = $2;''/eo));
+    $anchor = &missing_braces unless (
+	(s/$next_pair_pr_rx/$anchor=$2;''/eo)
+	||(s/$next_pair_rx/$anchor=$2;''/eo));
+    $name = &missing_braces unless (
+	(s/$next_pair_pr_rx/$name=$2;''/eo)
+	||(s/$next_pair_rx/$name=$2;''/eo));
+    $text = &missing_braces unless (
+	(s/$next_pair_pr_rx/$text=$2;''/eo)
+	||(s/$next_pair_rx/$text=$2;''/eo));
+    $anchor = $anchor . '.' . $name   if $name   ne '';
+    $url    = $url    . '#' . $anchor if $anchor ne '';
+    # and recode the ~ (don't turn it to space)
+    $url =~ s/~/~/go;
+    "$text".$_;
+}
+
+sub process_hyperref {
+    local($ref_mark, $label) = @_;
+    local($id, $text);
+    $text = &missing_braces unless
+	((s/$next_pair_pr_rx/($id, $text) = ($1, $2); ''/eo)
+	||(s/$next_pair_rx/($id, $text) = ($1, $2); ''/eo));
+    $label =~ s/<[^>]*>//go ; #RRM: Remove any HTML tags
+    $label =~ s/$label_rx/_/g;	# replace non alphanumeric characters
+
+    print "\nLINK: $ref_mark\#$label\#$id  :$text:" if ($VERBOSITY > 3);
+
+    # The quotes around the HREF are inserted later
+    join('',"$text<\/A>", $_);
+}
+
 1;
diff -Naur latex2html-2018.2.orig/styles/listings.perl latex2html-2018.2/styles/listings.perl
--- latex2html-2018.2.orig/styles/listings.perl	1970-01-01 07:00:00.000000000 +0700
+++ latex2html-2018.2/styles/listings.perl	2020-11-10 21:37:14.840074735 +0700
@@ -0,0 +1,520 @@
+# -*- perl -*-
+#
+# $Id: verbatim.perl,v 1.10 2001/11/29 21:44:13 RRM Exp $
+#
+# listings.perl
+#   Georgy Salnikov  13/10/20
+#
+# Extension to LaTeX2HTML V2018 to partly support the "listings" package.
+#
+# Partly derived from verbatim.perl and verbatimfiles.perl
+#
+# Change Log:
+# ===========
+#
+# $Log: verbatim.perl,v $
+#
+# Note:
+# This module provides translation for the \lstlisting environment
+# and for some more commands of the listings.sty package
+#
+# Handling decisions are done together with verbatim by LaTeX2HTML main program
+#
+# Several global variables and arrays are defined in the initialization block
+# of the LaTeX2HTML main program:
+#
+# %lstset_current
+# %lstset_style
+# $lst_name
+# $lst_last_counter
+# %lst_auto_counter
+#
+
+package main;
+
+# This package very probably may be used in listings
+&do_require_package("color");
+
+# Implementation of \lstinputlisting[]{}, preparation only
+sub do_cmd_lstinputlisting {
+  local($_) = @_;
+  local($outer,$file);
+
+  local($dum,$option) = &get_verb_optional_argument;
+  $file = &missing_braces unless (
+    (s/$next_pair_pr_rx/$file=$2;''/eo)
+    ||(s/$next_pair_rx/$file=$2;''/eo));
+  $outer = $_;
+
+  local($closures,$reopens) = &preserve_open_tags;
+  my ($verb_pre, $verb_post) = ('','
');
+  if ($USING_STYLES) {
+    $env_id .= ' CLASS="verbatim"' unless ($env_id =~ /(^|\s)CLASS\s*\=/i);
+    $verb_pre =~ s/>/ $env_id>/;
+  }
+
+  # %verbatim not coupled to a dbm => will not work in subprocesses, but don't mind
+  $verbatim{++$global{'verbatim_counter'}} = $option.$file;
+
+  # Do nothing here, just wrap into a verbatim-like lstlisting environment.
+  # File reading and decorating postponed to &process_lstlisting.
+  join('', $closures, $verb_pre
+       , $verbatim_mark, 'lstfile', $global{'verbatim_counter'}
+       , '#', $verb_post, $reopens, $outer);
+}
+
+# Implementation of \lstset{}, preparation only
+sub do_cmd_lstset {
+  local($_) = @_;
+  local($option) = &missing_braces unless (
+    (s/$next_pair_pr_rx/$option=$2;''/eo)
+    ||(s/$next_pair_rx/$option=$2;''/eo));
+  local($outer) = $_;
+
+  # In preamble this just initializes the defaults
+  if ($PREAMBLE) {
+    my(%opts) = &lst_parse_options($option);
+    @lstset_current{keys %opts} = (values %opts);
+    return $outer;
+  }
+
+  # Do nothing here, just wrap into a verbatim-like lstset environment.
+  # Processing options will be done later by &process_lstlisting.
+  local($closures,$reopens) = &preserve_open_tags;
+  $verbatim{++$global{'verbatim_counter'}} = $option;
+  join('', $closures, $verbatim_mark, 'lstset', $global{'verbatim_counter'},
+       '#', $reopens, $outer);
+}
+
+# Implementation of \lstdefinestyle{}{}, preparation only
+sub do_cmd_lstdefinestyle {
+  local($_) = @_;
+  local($style) = &missing_braces unless (
+    (s/$next_pair_pr_rx/$style=$2;''/eo)
+    ||(s/$next_pair_rx/$style=$2;''/eo));
+  local($option) = &missing_braces unless (
+    (s/$next_pair_pr_rx/$option=$2;''/eo)
+    ||(s/$next_pair_rx/$option=$2;''/eo));
+  local($outer) = $_;
+
+  # In preamble this just creates the new style
+  if ($PREAMBLE) {
+    my(%opts) = &lst_parse_options($option);
+    @{$lstset_style{$style}}{keys %opts} = (values %opts);
+    return $outer;
+  }
+
+  # Do nothing here, just wrap into a verbatim-like lststyle environment.
+  # Processing options will be done later by &process_lstlisting.
+  local($closures,$reopens) = &preserve_open_tags;
+  $verbatim{++$global{'verbatim_counter'}} = join(',', $style, $option);
+  join('', $closures, $verbatim_mark, 'lststyle', $global{'verbatim_counter'},
+       '#', $reopens, $outer);
+}
+
+# Implementation of \lstname, not much to do
+sub do_cmd_lstname { $lst_name . $_[0]; }
+
+# This is the main driver subroutine for the lstlisting environment
+sub process_lstlisting {
+  local($lst_pre, $lst_post, $lst_cmd, $_) = @_;
+
+  # Check if it was an auxiliary command wrapped into an environment
+  if ($lst_cmd eq 'lstset')   { return &set_lstset($_);   }
+  if ($lst_cmd eq 'lststyle') { return &set_lststyle($_); }
+
+  # Process an actual lstlisting environment
+  local($option,$dum) = &get_verb_optional_argument;
+  local($contents) = $_;
+
+  # Fetch current defaults and apply specified options...
+  my(%curopts) = %lstset_current;
+  my(%opts) = &lst_parse_options($option);
+
+  # First of all, apply either specified or default style if given
+  if ($opts{'style'} ne '') {
+    @curopts{keys %{$lstset_style{$opts{'style'}}}} =
+      (values %{$lstset_style{$opts{'style'}}});
+  }
+  elsif ($lstset_current{'style'} ne '') {
+    @curopts{keys %{$lstset_style{$lstset_current{'style'}}}} =
+      (values %{$lstset_style{$lstset_current{'style'}}});
+  }
+
+  # Now apply all the other specified options
+  @curopts{keys %opts} = (values %opts);
+
+  $lst_name = $curopts{'name'};
+
+  # For wrapped lstinputlisting - replace file name with file contents
+  if ($lst_cmd eq 'lstfile') {
+    my($dir);
+    my($file) = $contents;
+    $lst_name = $file;
+    my($file2) = "$file.tex";
+    if ($file !~ /\.tex$/) {
+      # 2nd choice is better than 1st - TeXnical quirk
+      ($file,$file2) = ($file2,$file);
+    }
+    my($inputpath) = $curopts{'inputpath'};
+    my($found) = 0;
+    foreach $dir ("$texfilepath", split(/:/,$ENV{'TEXINPUTS'})) { 
+      if (-f ($_ = "$dir/$inputpath/$file") ||
+	  -f ($_ = "$dir/$inputpath/$file2") ||
+	  -f ($_ = "$dir/$file") ||
+	  -f ($_ = "$dir/$file2")) {
+	$found = 1;
+	# overread $_ with file contents
+	&slurp_input($_);
+	last;
+      }
+    }
+    &write_warnings("No file <$file> for lstinputlisting.") unless $found;
+    # pre_process file contents
+    if (defined &replace_all_html_special_chars) {
+      &replace_all_html_special_chars;
+    } else {
+      &replace_html_special_chars;
+    }
+    s/\n$//;		# vertical space is contributed by  already.
+    $contents = $_;
+  }
+
+  # Interpret the rest of options in sequence...
+  # Line numbering (numbers,stepnumber,numberfirstline,numbersep,firstnumber)
+  my($i, $counter, $cline, $nlines);
+  $cline = '';
+  $i = $counter = $nlines = 0;
+  my($step) = sprintf("%.0f", $curopts{'stepnumber'});
+  my($incr) = $step<=>0;
+  $curopts{'numbers'} = 'none' if (! $incr);
+  my($nspaces);
+  ($nspaces, $dum) = &convert_length ($curopts{'numbersep'}, 1);
+  $nspaces = sprintf("%.0f", $nspaces/10);
+  my($fcount) = $curopts{'firstnumber'};
+  if ($fcount eq 'auto') {
+    $fcount = 1;
+    if ($lst_name ne '' && $curopts{'numbers'} ne 'none') {
+      $lst_auto_counter{$lst_name} = 1
+	unless (exists ($lst_auto_counter{$lst_name}));
+      $fcount = $lst_auto_counter{$lst_name};
+    }
+  } elsif ($fcount eq 'last') {
+    $fcount = $lst_last_counter;
+  } else {
+    $fcount = sprintf("%.0f", $fcount);
+  }
+
+  # Captioning options group (title, caption, captionpos)
+  my($title, $caption, $cappos);
+  $cappos = 'TOP';
+  $cappos = 'BOTTOM' if ($curopts{'captionpos'} eq 'b');
+  $title  = '';
+  if ($curopts{'title'} ne '') {
+    $_ = $curopts{'title'};
+    # Replace braces with marks and try to interpret this as bracketed command
+    &lst_translate_option;
+    &replace_markers;
+    s/^\s+//;
+    s/\s+$//;
+    $title = $_;
+  }
+  $caption = '';
+  if ($curopts{'caption'} ne '') {
+    $_ = $curopts{'caption'};
+    # Replace braces with marks and try to interpret this as bracketed command
+    &lst_translate_option;
+    &replace_markers;
+    # Evtl separate short and full captions
+    ($option,$dum) = &get_verb_optional_argument;
+    s/^\s+//;
+    s/\s+$//;
+    $_ = $option if ($_ eq '');
+    s/^\s+//;
+    s/\s+$//;
+    $caption = $_;
+  }
+  # If caption empty, use title
+  if ($caption ne '') {
+    $caption = 'Listing: ' . $caption;
+  } else {
+    $caption = $title;
+  }
+
+  # basicstyle option: convert it to a pair of opening and closing HTML tags
+  my($bstyle_open, $bstyle_close);
+  $_ = $curopts{'basicstyle'};
+  # Replace braces with marks and try to interpret this as bracketed command
+  &lst_translate_option;
+  s/$O\d+$C//go;		# Get rid of bracket id's
+  s/$OP\d+$CP//go;		# Get rid of processed bracket id's
+  $bstyle_open = $bstyle_close = ''
+    unless (s/^\s*((<\w+[^>]*>\s*)+)[^<]*((<\/\w+>\s*)+)$/$bstyle_open=$1;$bstyle_close=$3;''/eo);
+  $bstyle_open  = $bstyle_open."\n"  if ($bstyle_open  ne '');
+  $bstyle_close = "\n".$bstyle_close if ($bstyle_close ne '');
+
+  # Framing options group (frame, framesep, framerule)
+  my($frame) = 'VOID';
+  $frame = 'LHS'    if ($curopts{'frame'} eq 'leftline');
+  $frame = 'ABOVE'  if ($curopts{'frame'} eq 'topline');
+  $frame = 'BELOW'  if ($curopts{'frame'} eq 'bottomline');
+  $frame = 'HSIDES' if ($curopts{'frame'} eq 'lines');
+  $frame = 'BORDER'
+    if ($curopts{'frame'} eq 'single' || $curopts{'frame'} eq 'shadowbox');
+  if ($curopts{'frame'} =~ /^[trblTRBL]+$/) {
+    my($t, $r, $b, $l, $c);
+    foreach $c (split //, $curopts{'frame'}) {
+      $t = 1 if ("\L$c" eq 't');
+      $r = 1 if ("\L$c" eq 'r');
+      $b = 1 if ("\L$c" eq 'b');
+      $l = 1 if ("\L$c" eq 'l');
+    }
+    if ($t && $r && $b && $l) { $frame = 'BORDER'; }
+    elsif ($t && $b)          { $frame = 'HSIDES'; }
+    elsif ($r && $l)          { $frame = 'VSIDES'; }
+    elsif ($t)                { $frame = 'ABOVE';  }
+    elsif ($b)                { $frame = 'BELOW';  }
+    elsif ($l)                { $frame = 'LHS';    }
+    elsif ($r)                { $frame = 'RHS';    }
+  }
+  my($framesep);
+  ($framesep, $dum) = &convert_length ($curopts{'framesep'}, 1);
+  $framesep = sprintf("%.0f", $framesep);
+  my($framerule);
+  ($framerule, $dum) = &convert_length ($curopts{'framerule'}, 1);
+  $framerule = sprintf("%.0f", $framerule);
+  if ($framerule == 1) { # Special case specifying a very thin border
+    $framerule = '';
+  } else {
+    $framerule = " BORDER=\"$framerule\"";
+  }
+
+  # Coloring options group (backgroundcolor, rulecolor)
+  my($bgcolor);
+  $_ = $curopts{'backgroundcolor'};
+  if (/^\s*\\/) {
+    # Translate as a command and extract color value from the HTML tag
+    &lst_translate_option;
+    s/$O\d+$C//go;		# Get rid of bracket id's
+    s/$OP\d+$CP//go;		# Get rid of processed bracket id's
+    # Extract color value or clear the malformed contents
+    $_ = ''
+      unless (s/^\s*\s*<\/FONT>\s*$//);
+  } else {
+    # Must be a \color command
+    $_ = '';
+  }
+  $bgcolor = $_;
+  $bgcolor = " BGCOLOR=\"$bgcolor\"" unless ($bgcolor eq '');
+  my($rulecolor);
+  $_ = $curopts{'rulecolor'};
+  if (/^\s*\\/) {
+    # Translate as a command and extract color value from the HTML tag
+    &lst_translate_option;
+    s/$O\d+$C//go;		# Get rid of bracket id's
+    s/$OP\d+$CP//go;		# Get rid of processed bracket id's
+    # Extract color value or clear the malformed contents
+    $_ = ''
+      unless (s/^\s*\s*<\/FONT>\s*$//);
+  } else {
+    # Must be a \color command
+    $_ = '';
+  }
+  $rulecolor = $_;
+  $rulecolor = " BORDERCOLOR=\"$rulecolor\"" unless ($rulecolor eq '');
+
+  $lst_pre  = $bstyle_open.$lst_pre."\n";
+  $lst_post = "\n".$lst_post.$bstyle_close;
+  $_ = $contents;
+
+  # Evtl generate line numbers
+  if ($curopts{'numbers'} eq 'left') {
+    # Insert numbers from the left side.
+    $counter = $fcount-$incr;
+    s/^/$i++; $counter+=$incr;
+    (($counter % $step) && ($curopts{'numberfirstline'} ne 'true' || $i > 1)) ?
+      ('     ' . ' ' x $nspaces) :
+      (sprintf("%5d",$counter) . ' ' x $nspaces)/mge;
+    $lst_last_counter = $counter+$incr;
+    $lst_auto_counter{$lst_name} = $lst_last_counter
+      if ($curopts{'firstnumber'} eq 'auto' && $lst_name ne '');
+  }
+  elsif ($curopts{'numbers'} eq 'right' && $HTML_VERSION > 2.1) {
+    # Inserting right padded numbers for every line is tricky.
+    # Do it as a table with two huge columns with verbatim contents.
+    # But only if HTML version is high enough...
+    s/$/$nlines++;''/mge;
+    for ($counter=$fcount; $i<$nlines; $i++) {
+      $cline .= (($counter % $step) &&
+		 ($curopts{'numberfirstline'} ne 'true' || $i > 0)) ?
+	(' ' x $nspaces . "     \n") :
+	(' ' x $nspaces . sprintf("%d\n",$counter));
+      $counter += $incr;
+    }
+    $lst_last_counter = $counter;
+    $lst_auto_counter{$lst_name} = $lst_last_counter
+      if ($curopts{'firstnumber'} eq 'auto' && $lst_name ne '');
+    $cline =~ s/\n$//;
+    $_ = "| "
+      .$lst_pre.$_.$lst_post." | "
+      .$lst_pre.$cline.$lst_post." | 
";
+    $lst_pre = $lst_post = '';
+  }
+
+  # Frames, captions and coloring are generated also as a synthetic table
+  if ($HTML_VERSION > 2.1) {
+    $lst_pre = ""
+      .(($caption ne '') ?
+	("\n".$caption."\n") : '')
+      ."| \n".$lst_pre;
+    $lst_post = $lst_post."\n | 
";
+  }
+
+  # WHEW !!!
+  $lst_pre.$_.$lst_post."\n";
+}
+
+# Driver subroutine for the real processing of \lstinline command
+sub process_lstinline {
+  local($_, $contents) = @_;
+
+  # Get lstinline options
+  local($option,$dum) = &get_verb_optional_argument;
+
+  # Fetch current defaults and apply specified options...
+  my(%curopts) = %lstset_current;
+  my(%opts) = &lst_parse_options($option);
+
+  # First of all, apply either specified or default style if given
+  if ($opts{'style'} ne '') {
+    @curopts{keys %{$lstset_style{$opts{'style'}}}} =
+      (values %{$lstset_style{$opts{'style'}}});
+  }
+  elsif ($lstset_current{'style'} ne '') {
+    @curopts{keys %{$lstset_style{$lstset_current{'style'}}}} =
+      (values %{$lstset_style{$lstset_current{'style'}}});
+  }
+
+  # Now apply all the other specified options
+  @curopts{keys %opts} = (values %opts);
+
+  # basicstyle seems the only option which can be really useful for lstinline
+  my($bstyle_open, $bstyle_close);
+  $_ = $curopts{'basicstyle'};
+  # Replace braces with marks and try to interpret this as bracketed command
+  &lst_translate_option;
+  s/$O\d+$C//go;		# Get rid of bracket id's
+  s/$OP\d+$CP//go;		# Get rid of processed bracket id's
+  $bstyle_open = $bstyle_close = ''
+    unless (s/^\s*((<\w+[^>]*>\s*)+)[^<]*((<\/\w+>\s*)+)$/$bstyle_open=$1;$bstyle_close=$3;''/eo);
+
+  # Make the actual lstinline output
+  $bstyle_open.''.$contents.''.$bstyle_close;
+}
+
+# Driver subroutine for \lstset processing
+# Just copy the given set of options to the default options set
+sub set_lstset {
+  local($_) = @_;
+  my(%opts) = &lst_parse_options($_);
+  @lstset_current{keys %opts} = (values %opts);
+  '';
+}
+
+# Driver subroutine for \lstdefinestyle
+# Similar to the preceding but maintains distinct named options sets (styles)
+sub set_lststyle {
+  local($_) = @_;
+  local($style) = '' unless (s/^(\w+),/$style=$1;''/eo);
+  return '' if ($style eq '');
+  my(%opts) = &lst_parse_options($_);
+  @{$lstset_style{$style}}{keys %opts} = (values %opts);
+  '';
+}
+
+# Option parser for lstlisting commands/envs family, this can be tricky
+sub lst_parse_options {
+  local($_) = @_;
+
+  # First get rid of comment marks
+  s/(\\\w+)$comment_mark\d*\s*?\n[ \t]*/$1 \n/go;
+  s/($comment_mark\d*\s*)+\n[ \t]*/\n/go;
+
+  # Find any kinds of brackets to keep them intact
+  # for the case if they might have commas inside
+  my(@fields, @chunks);
+  my($before, $match, $after);
+  while (/$any_next_pair_pr_rx|$any_next_pair_rx4|$opt_arg_rx|$verb_braces_rx/)
+  {
+    ($before, $match, $after) = ($`, $&, $');
+    @chunks = split(/,/, $before);
+    $fields[$#fields] .= shift(@chunks) if @fields;
+    push(@fields, @chunks);
+    $fields[$#fields] .= $match;
+    $_ = $after;
+  }
+  @chunks = split(/,/);
+  $fields[$#fields] .= shift(@chunks) if @fields;
+  push(@fields, @chunks);
+
+  # All options are separated, now split them to option name and value
+  my(%opts);
+  my($par, $val);
+  foreach (@fields) {
+    next unless ($par, $val) = /^\s*(\w+?)\s*=\s*(.*)\s*$/s;
+    $opts{$par} = $val;
+  }
+  %opts;
+}
+
+# Replace braces with marks and try to interpret this as a bracketed command
+sub lst_translate_option {
+  # Modifies $_
+  &mark_string($_);
+  my($br_id) = ++$global{'max_id'};
+  $_ = $O.$br_id.$C.$_.$O.$br_id.$C;
+  $_ = &translate_environments($_);
+  $_ = &translate_commands($_);
+}
+
+# &get_next_optional_argument is not well suited for lstlisting
+# Here is a special version of &get_next_optional_argument
+sub get_verb_optional_argument {
+  local($next, $pat);
+  my($before, $match, $after);
+  $next = $pat = $match = '';
+  if (s/^(\[)/$pat=$1;''/eo) {
+    # Find any kinds of braces not to stuck on [] evtl nested between them
+    while (/$any_next_pair_pr_rx|$any_next_pair_rx4|$verb_braces_rx|\]/) {
+      ($before, $match, $after) = ($`, $&, $');
+      $next .= $before;
+      $pat  .= $before;
+      if ($match eq ']') {
+	$pat .= $match;
+	$_ = $after;
+	# Before returning, remove comment mark and newline after closing ]
+	s/^[ \t]*($comment_mark\d*[ \t]*)*\n//o;
+	last;
+      }
+      $next .= $match;
+      $pat  .= $match;
+      $_ = $after;
+    }
+    if ($match ne ']') {
+      # Closing ] not found, revert back $_ and clear arguments
+      $_ = $pat . $_;
+      $next = $pat = '';
+    }
+  }
+  # Imitate return of &get_next_optional_argument
+  ($next, $pat);
+}
+
+&process_commands_wrap_deferred (<<_RAW_ARG_DEFERRED_CMDS_);
+lstinputlisting # [] # {}
+lstdefinestyle # {} # {}
+lstset # {}
+_RAW_ARG_DEFERRED_CMDS_
+
+1;			# Must be last line
diff -Naur latex2html-2018.2.orig/styles/verbatimfiles.perl latex2html-2018.2/styles/verbatimfiles.perl
--- latex2html-2018.2.orig/styles/verbatimfiles.perl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/styles/verbatimfiles.perl	2020-11-10 21:35:31.090073210 +0700
@@ -80,8 +80,8 @@
 
     #insert numbers for every line
     #but not the first line if it's empty (LaTeX'ism?)
-    local($firstemptyline);
-    $firstemptyline = $1 if s/^([ \t]+\n)//;
+    local($first);
+    $first = $1 if s/^([ \t]+\n)//;
 
     #and not the last end of line
     s/\n$//;
diff -Naur latex2html-2018.2.orig/styles/wrapfig.perl latex2html-2018.2/styles/wrapfig.perl
--- latex2html-2018.2.orig/styles/wrapfig.perl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/styles/wrapfig.perl	2020-11-10 21:36:49.110074357 +0700
@@ -17,11 +17,15 @@
 sub do_env_wrapfigure{
     local($_) = @_;
 
-    $contents =~ s/$optional_arg_rx//o;	   # ditch [nlines]
-    $contents =~ s/$next_pair_rx//o;	   # ditch {placement}
-    $contents =~ s/$next_pair_rx//o;	   # ditch {width}
+    s/$optional_arg_rx//o;	   # ditch [nlines]
+    s/$next_pair_rx//o;		   # ditch {placement}
+    $wrapfigure_width = &missing_braces unless ( # save {width}
+      (s/$next_pair_pr_rx/$wrapfigure_width=$2;''/eo)
+      ||(s/$next_pair_rx/$wrapfigure_width=$2;''/eo));
 #   &process_environment("figure", $global{'max_id'}++);
-    &do_env_figure($_);
+    $_ = &do_env_figure($_);
+    $wrapfigure_width = '';	# clear width
+    $_;
 }
 
 &process_commands_in_tex (<<_RAW_ARG_CMDS_);
diff -Naur latex2html-2018.2.orig/texexpand.pin latex2html-2018.2/texexpand.pin
--- latex2html-2018.2.orig/texexpand.pin	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/texexpand.pin	2020-11-10 21:12:45.960053138 +0700
@@ -13,8 +13,9 @@
 # Jens Lippmann
 
 # Recognizes \documentclass, \documentstyle, \usepackage, \RequirePackage,
-# \begin{verbatim}...\end{verbatim}, %begin{latexonly}...%end{latexonly},
-# \begin{latexonly}...\end{latexonly}, \input, \include, \verb, \latex
+# \begin{verbatim}...\end{verbatim}, \begin{lstlisting}...\end{lstlisting},
+# %begin{latexonly}...%end{latexonly}, \begin{latexonly}...\end{latexonly},
+# \input, \include, \verb, \lstinline, \latex
 # \endinput, \end{document}
 # \includecomment, \excludecomment
 # \begin{"to exclude"}, \end{"to exclude"}
@@ -40,8 +41,9 @@
 #  c) \begin{any}  introduced with \excludecomment
 #  d) %begin{any}
 #  e) \begin{verbatim}
-#  f) \begin{latexonly}
-#  g) %begin{latexonly}
+#  f) \begin{lstlisting}
+#  g) \begin{latexonly}
+#  h) %begin{latexonly}
 # 
 # a)-d) cause texexpand to drop its contents, it will not show up in the
 # output file. You can use this to 'comment out' a bunch of files, say.
@@ -66,18 +68,18 @@
 # 3. [%\]begin{"to exclude"} and [%\]end{"to exclude"} have to be on a
 #    separate line.
 #    Anything between these tags (including the tags) is discarded.
-# 4. \begin{verbatim/verbatim*} and \end{verbatim/verbatim*} have to be
-#    on a separate line.
+# 4. \begin{verbatim/verbatim*/lstlisting} and
+#    \end{verbatim/verbatim*/lstlisting} have to be on a separate line.
 #    Anything between these tags (including the tags) is not expanded.
 # 5. The scope of any such tags may extend over several files.
 #    The opening tag for latexonly may occur on a different include level
 #    than the closing tag.
-#    The opening tag for verbatim/"to exclude" must occur within the same
-#    file than the closing tag.
+#    The opening tag for verbatim/lstlisting/"to exclude" must occur
+#    within the same file than the closing tag.
 # 6. Warnings are printed when the document has been parsed and open
 #    tags remain.
-# 7. When in a "to exclude"/verbatim environment, texexpand won't recognize
-#    ANY command except the corresponding closing tag.
+# 7. When in a "to exclude"/verbatim/lstlisting environment, texexpand
+#    won't recognize ANY command except the corresponding closing tag.
 #    There cannot be any nested constructions.
 #    This behaviour is identical to that of LaTeX.
 # 8. \begin{latexonly},\end{latexonly} may be nested, whereas
@@ -87,8 +89,9 @@
 #     has to be on a separate line.
 # 11. Everything behind a `%' that isn't preceded by a `\' is regarded as
 #     a comment, i.e. it is printed but not interpreted.
-# 12. If any command listed in 10. is preceded by an occurence of `\verb' or
-#    `\latex' then it is NOT interpreted. This crashes on lines like this:
+# 12. If any command listed in 10. is preceded by an occurence of
+#     `\verb', `\lstinline' or `\latex' then it is NOT interpreted.
+#     This crashes on lines like this:
 #        blah blah \verb+foo foo+ \input{bar} % bar won't be loaded!
 # 13. Packages provided via \usepackage are handled the same way as
 #    `options' in \document(class|style), i.e. they are included when
@@ -492,8 +495,8 @@
 # Create generic regexp's:
 # If this matches before a command, the command is ignored.
     $ignore_cmd_rx =
-#	'(\\latex\W|\\verb|\\expandafter|\\ifx|\\else\W|[\|\[\@]$)';
-  "(\\\\latex\\W|\\\\verb|\\\\expandafter|\\\\ifx|\\\\else\\W|[\\|\\[\\@]\$)";
+#	'(\\latex\W|\\verb|\\lstinline|\\expandafter|\\ifx|\\else\W|[\|\[\@]$)';
+  "(\\\\latex\\W|\\\\verb|\\\\lstinline|\\\\expandafter|\\\\ifx|\\\\else\\W|[\\|\\[\\@]\$)";
 # This matches a square bracket pair (typically an option list).
     $options_rx = '(\[[^\]]*\]|)';
 # This matches a single argument.
@@ -534,7 +537,7 @@
 
 
 sub main {
-# Note that verbatim/latexonly may split over different files!
+# Note that verbatim/lstlisting/latexonly may split over different files!
 # $verbatim is 1 if inside a verbatim environment,
 # $latexonly is > 0 if inside latexonly environments
 # $includelevel indicates the depth of include/input
@@ -597,17 +600,17 @@
 #     also would retain its body from LaTeX.
 #     => $active false, $mute true
 #  3) interprete minimal and pass the lines to the out file
-#     This is inside a verbatim or latexonly environment.
+#     This is inside a verbatim, lstlisting or latexonly environment.
 #     The line of course must be at least interpreted to
 #     determine the closing tag.
 #     => $active false, $mute false
 #
 # Any environment may extend over several include files.
-# Any environement except verbatim and latexonly may have its
+# Any environement except verbatim, lstlisting and latexonly may have its
 # opening or closing tag on different input levels.
-# The comment and verbatim environments cannot be nested, as
+# The comment and verbatim/lstlisting environments cannot be nested, as
 # is with LaTeX.
-# We must at least parse verbatim/comment environments in
+# We must at least parse verbatim/lstlisting/comment environments in
 # latexonly environments, to catch fake latexonly tags.
 #
 # The work scheme:
@@ -768,7 +771,7 @@
 	    $mute=1 unless $latexonly;
 	}
 #	elsif (!$fakeenv && !$verbatim && /\\begin\s*\{\s*verbatim(\*)?\s*\}/) {
-	elsif (!$fakeenv && !$verbatim && /\\begin\s*\{\s*(\w*[Vv]erbatim\w*\*?)\s*\}/) {
+	elsif (!$fakeenv && !$verbatim && /\\begin\s*\{\s*((\w*[Vv]erbatim\w*|lstlisting)\*?)\s*\}/) {
 	    ($before,$verbatimname) = ($`,$1);
 	    ($active,$verbatim) = (0,1)
 		unless ($before =~ /$ignore_cmd_rx/o);
@@ -840,7 +843,7 @@
 	    }
 	}
 #	elsif ( /\\end\s*\{\s*verbatim(\*)?\s*\}/) {
-	elsif ( /\\end\s*\{\s*(\w*[Vv]erbatim\w*\*?)\s*\}/) {
+	elsif ( /\\end\s*\{\s*((\w*[Vv]erbatim\w*|lstlisting)\*?)\s*\}/) {
 	    if ($1 eq $verbatimname) {
 		$verbatim=0;
 		$active = ($latexonly ? 0 : 1);
diff -Naur latex2html-2018.2.orig/versions/html3_2.pl latex2html-2018.2/versions/html3_2.pl
--- latex2html-2018.2.orig/versions/html3_2.pl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/versions/html3_2.pl	2020-11-10 21:40:11.570077334 +0700
@@ -580,13 +580,14 @@
     s/\\\\\s*\[([^]]+)\]/\\\\/g;  # TKM - get rid of [N.n pc] on end of rows...
     s/\\newline\s*\[([^]]+)\]/\\newline/g;
     s/\n\s*\n/\n/g;	# Remove empty lines (otherwise will have paragraphs!)
+    s/\\\\\s*\n\s*$par_rx\s*\n/\\\\\n/g; # don't start next row with a \par
     local($i,@colspec,$char,$cols,$cell,$htmlcolspec,$frames,$rules);
     local(@rows,@cols,$border,$frame);
     local($colspan,$cellcount);
-    
+
     # set a flag to indicate whether there are any \multirow cells
     my $has_multirow = 1 if (/\\multirow/s);
-    
+
     # convert \\s inside \parbox commands to \newline s;
     # catch nestings
 
@@ -607,7 +608,7 @@
 
     if ($color_env) {
 	local($color_test) = join(',',@$open_tags_R);
-	if ($color_test =~ /(color\{[^}]*})/g ) {
+	if ($color_test =~ /(color\{[^}]*\})/g ) {
 	    $color_env = $1;
 	}
     }
@@ -693,7 +694,7 @@
 	$return = join('', "")
+		, $tab_width, ">");
     }
     local($firstrow) = 1;
     local($lastrow) = '';
@@ -736,8 +737,11 @@
 
 	for ( $i = 0; $i <= $#colspec; $i++ ) {
 	    # skip this cell if it is covered by a \multirow
-	    next if ($has_multirow && @row_spec[$i] > 0);
-	    
+	    if ($has_multirow && @row_spec[$i] > 0) {
+	        shift(@cols);
+	        next;
+	    }
+
 	    $colspec = $colspec[$i];
 	    if (!($colspec =~ $content_mark)) {
 		# no data required in this column
@@ -899,7 +903,7 @@
 }
 sub do_cmd_multirow {
     local($_) = @_;
-    local($dmy1,$dmy2,$dmy3,$dmy4,$spanrows,$pxs,$rwidth,$valign,$vspec,$text);
+    local($dmy1,$dmy2,$dmy3,$dmy4,$spanrows,$pxs,$rwidth,$text);
     $spanrows = &missing_braces unless (
 	(s/$next_pair_pr_rx/$spanrows=$2;''/eo)
         ||(s/$next_pair_rx/$spanrows=$2;''/eo));
@@ -910,25 +914,21 @@
     $colspec =~ /^<([A-Z]+)/;
     local($celltag) = $1;
 
-    # read the width, save it for later use
+    # read the width, save it for later use (the last regex matches '*')
     $rwidth = &missing_braces unless (
 	(s/$next_pair_pr_rx/$rwidth=$2;''/eo)
-	||(s/$next_pair_rx/$rwidth=$2;''/eo));
-
-    # catch cases where the \textwidth has been discarded
-    $rwidth =~s!^(\w)($OP\d+$CP)\s*(\d|\d*\.\d+)\2$!$1\{$3\\textwidth\}!;
+	||(s/$next_pair_rx/$rwidth=$2;''/eo)
+	||(s/^[\s%]*(\*)($comment_mark\d*\n?)?/$rwidth=$1;''/eo));
 
-    ($pxs,$rwidth) = &convert_length($rwidth);
-
-    $valign = &missing_braces unless (
-        (s/$next_pair_pr_rx/$valign=$2;''/eo)
-        ||(s/$next_pair_rx/$valign=$2;''/eo));
-    $vspec = ' VALIGN="TOP"' if $valign;
-    if ($valign =~ /m/i) { $vspec =~ s/TOP/MIDDLE/ }
-    elsif ($valign =~ /b/i) { $vspec =~ s/TOP/BOTTOM/ }
-
-    $colspec =~ s/VALIGN="\w+"// if $vspec; # avoid duplicate tags
-    $colspec =~ s/>$content_mark/$vspec ROWSPAN=$rowspan WIDTH=$pxs$&/;
+    if ($rwidth eq '*') {
+        # automatic width
+	$colspec =~ s/>$content_mark/ ROWSPAN=$rowspan$&/;
+    } else {
+        # catch cases where the \textwidth has been discarded
+        $rwidth =~s!^(\w)($OP\d+$CP)\s*(\d|\d*\.\d+)\2$!$1\{$3\\textwidth\}!;
+	($pxs,$rwidth) = &convert_length($rwidth);
+	$colspec =~ s/>$content_mark/ ROWSPAN=$rowspan WIDTH=$pxs$&/;
+    }
 
     s/$next_pair_pr_rx/$text=$2;''/eo;
     $text = &translate_commands($text) if ($text =~ /\\/);
diff -Naur latex2html-2018.2.orig/versions/html4_01.pl latex2html-2018.2/versions/html4_01.pl
--- latex2html-2018.2.orig/versions/html4_01.pl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/versions/html4_01.pl	2020-11-10 21:42:09.380079066 +0700
@@ -949,13 +949,14 @@
     s/\\\\\s*\[([^]]+)\]/\\\\/g;  # TKM - get rid of [N.n pc] on end of rows...
     s/\\newline\s*\[([^]]+)\]/\\newline/g;
     s/\n\s*\n/\n/g;	# Remove empty lines (otherwise will have paragraphs!)
+    s/\\\\\s*\n\s*$par_rx\s*\n/\\\\\n/g; # don't start next row with a \par
     local($i,@colspec,$char,$cols,$cell,$htmlcolspec,$frames,$rules);
     local(@rows,@cols,$border,$frame);
     local($colspan,$cellcount);
-    
+
     # set a flag to indicate whether there are any \multirow cells
     my $has_multirow = 1 if (/\\multirow/s);
-    
+
     # convert \\s inside \parbox commands to \newline s;
     # catch nestings
 
@@ -976,7 +977,7 @@
 
     if ($color_env) {
 	local($color_test) = join(',',@$open_tags_R);
-	if ($color_test =~ /(color\{[^}]*})/g ) {
+	if ($color_test =~ /(color\{[^}]*\})/g ) {
 	    $color_env = $1;
 	}
     }
@@ -1109,7 +1110,10 @@
 
 	for ( $i = 0; $i <= $#colspec; $i++ ) {
 	    # skip this cell if it is covered by a \multirow
-	    next if ($has_multirow && @row_spec[$i] > 0);
+	    if ($has_multirow && @row_spec[$i] > 0) {
+	        shift(@cols);
+	        next;
+	    }
 
 	    $colspec = $colspec[$i];
 	    if (!($colspec =~ $content_mark)) {
@@ -1268,7 +1272,7 @@
 }
 sub do_cmd_multirow {
     local($_) = @_;
-    local($dmy1,$dmy2,$dmy3,$dmy4,$spanrows,$pxs,$rwidth,$valign,$vspec,$text);
+    local($dmy1,$dmy2,$dmy3,$dmy4,$spanrows,$pxs,$rwidth,$text);
     $spanrows = &missing_braces unless (
 	(s/$next_pair_pr_rx/$spanrows=$2;''/eo)
         ||(s/$next_pair_rx/$spanrows=$2;''/eo));
@@ -1279,25 +1283,21 @@
     $colspec =~ /^<([A-Z]+)/;
     local($celltag) = $1;
 
-    # read the width, but ignore it
+    # read the width, save it for later use (the last regex matches '*')
     $rwidth = &missing_braces unless (
 	(s/$next_pair_pr_rx/$rwidth=$2;''/eo)
-	||(s/$next_pair_rx/$rwidth=$2;''/eo));
+	||(s/$next_pair_rx/$rwidth=$2;''/eo)
+	||(s/^[\s%]*(\*)($comment_mark\d*\n?)?/$rwidth=$1;''/eo));
 
-    # catch cases where the \textwidth has been discarded
-    $rwidth =~s!^(\w)($OP\d+$CP)\s*(\d|\d*\.\d+)\2$!$1\{$3\\textwidth\}!;
-
-    ($pxs,$rwidth) = &convert_length($rwidth);
-
-    $valign = &missing_braces unless (
-        (s/$next_pair_pr_rx/$valign=$2;''/eo)
-        ||(s/$next_pair_rx/$valign=$2;''/eo));
-    $vspec = ' VALIGN="TOP"' if $valign;
-    if ($valign =~ /m/i) { $vspec =~ s/TOP/MIDDLE/ }
-    elsif ($valign =~ /b/i) { $vspec =~ s/TOP/BOTTOM/ }
-
-    $colspec =~ s/VALIGN="\w+"// if $vspec; # avoid duplicate tags
-    $colspec =~ s/>$content_mark/$vspec ROWSPAN=$rowspan WIDTH=$pxs$&/;
+    if ($rwidth eq '*') {
+        # automatic width
+	$colspec =~ s/>$content_mark/ ROWSPAN=$rowspan$&/;
+    } else {
+        # catch cases where the \textwidth has been discarded
+        $rwidth =~s!^(\w)($OP\d+$CP)\s*(\d|\d*\.\d+)\2$!$1\{$3\\textwidth\}!;
+	($pxs,$rwidth) = &convert_length($rwidth);
+	$colspec =~ s/>$content_mark/ ROWSPAN=$rowspan WIDTH=$pxs$&/;
+    }
 
     $text = &styled_text_chunk('SPAN','mrow','','','','', $_);
     $text = &translate_commands($text) if ($text =~ /\\/);
@@ -1356,7 +1356,7 @@
 	    , ''
             , &process_undefined_environment("tex2html_wrap_inline", $id, $saved)
 	    , '' );
-    } else { $_ = join('', $comment, $labels, $_ ) }
+    } else { $_ = join('', $comment, $labels, $_ ); }
     if ($border||($attributes)) {
         &make_table( $border, $attribs, '', '', '', $_ )
     } else { $_ }
diff -Naur latex2html-2018.2.orig/versions/html4_0.pl latex2html-2018.2/versions/html4_0.pl
--- latex2html-2018.2.orig/versions/html4_0.pl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/versions/html4_0.pl	2020-11-10 21:41:14.280078256 +0700
@@ -79,7 +79,7 @@
 $coord_type = "\^\\s*(\$|\\d+(\\s*,\\s*\\d+)+\\s*\$)";  # comma-separated list of numbers
 $pixel_type = "\^\\d+\$";
 $length_type = "\^\\d+\%?\$";
-$name_type = "\^[A-Za-z]([A-Za-z\\d\\-\\.]*\$";
+$name_type = "\^[A-Za-z][A-Za-z\\d\\-\\.]*\$";
 $string_type = $URL_type = $script_type = $ISOdate_type = $Internet_type =
 	$CDATA_type = "\^.*\$";
 $id_list_type = "\^\\s*\$name_type(\\s+$name_type)*\\s*\$";
@@ -928,6 +928,7 @@
     s/\\\\\s*\[([^]]+)\]/\\\\/g;  # TKM - get rid of [N.n pc] on end of rows...
     s/\\newline\s*\[([^]]+)\]/\\newline/g;
     s/\n\s*\n/\n/g;	# Remove empty lines (otherwise will have paragraphs!)
+    s/\\\\\s*\n\s*$par_rx\s*\n/\\\\\n/g; # don't start next row with a \par
     local($i,@colspec,$char,$cols,$cell,$htmlcolspec,$frames,$rules);
     local(@rows,@cols,$border,$frame);
     local($colspan,$cellcount);
@@ -1080,15 +1081,18 @@
 	$return .= "\n";
 	@cols = split(/$html_specials{'&'}/o);
 
-	if ($has_multirow) { 
+	if ($has_multirow) {
 	    # decrement the counters for multi-row cells 
 	    my @trow_spec = map {$_>0? --$_ : 0 } @row_spec;
 	    @row_spec = @trow_spec;
 	}
- 
+
 	for ( $i = 0; $i <= $#colspec; $i++ ) {
 	    # skip this cell if it is covered by a \multirow
-	    next if ($has_multirow && @row_spec[$i] > 0);
+	    if ($has_multirow && @row_spec[$i] > 0) {
+	        shift(@cols);
+	        next;
+	    }
 
 	    $colspec = $colspec[$i];
 	    if (!($colspec =~ $content_mark)) {
@@ -1247,7 +1251,7 @@
 }
 sub do_cmd_multirow {
     local($_) = @_;
-    local($dmy1,$dmy2,$dmy3,$dmy4,$spanrows,$pxs,$rwidth,$valign,$vspec,$text);
+    local($dmy1,$dmy2,$dmy3,$dmy4,$spanrows,$pxs,$rwidth,$text);
     $spanrows = &missing_braces unless (
 	(s/$next_pair_pr_rx/$spanrows=$2;''/eo)
         ||(s/$next_pair_rx/$spanrows=$2;''/eo));
@@ -1258,25 +1262,21 @@
     $colspec =~ /^<([A-Z]+)/;
     local($celltag) = $1;
 
-    # read the width, save it for later use
+    # read the width, save it for later use (the last regex matches '*')
     $rwidth = &missing_braces unless (
 	(s/$next_pair_pr_rx/$rwidth=$2;''/eo)
-	||(s/$next_pair_rx/$rwidth=$2;''/eo));
-
-    # catch cases where the \textwidth has been discarded
-    $rwidth =~s!^(\w)($OP\d+$CP)\s*(\d|\d*\.\d+)\2$!$1\{$3\\textwidth\}!;
+	||(s/$next_pair_rx/$rwidth=$2;''/eo)
+	||(s/^[\s%]*(\*)($comment_mark\d*\n?)?/$rwidth=$1;''/eo));
 
-    ($pxs,$rwidth) = &convert_length($rwidth);
-
-    $valign = &missing_braces unless (
-	(s/$next_pair_pr_rx/$valign=$2;''/eo)
-	||(s/$next_pair_rx/$valign=$2;''/eo));
-    $vspec = ' VALIGN="TOP"' if $valign;
-    if ($valign =~ /m/i) { $vspec =~ s/TOP/MIDDLE/ }
-    elsif ($valign =~ /b/i) { $vspec =~ s/TOP/BOTTOM/ }
-
-    $colspec =~ s/VALIGN="\w+"// if $vspec; # avoid duplicate tags
-    $colspec =~ s/>$content_mark/$vspec ROWSPAN=$rowspan WIDTH=$pxs$&/;
+    if ($rwidth eq '*') {
+        # automatic width
+	$colspec =~ s/>$content_mark/ ROWSPAN=$rowspan$&/;
+    } else {
+        # catch cases where the \textwidth has been discarded
+        $rwidth =~s!^(\w)($OP\d+$CP)\s*(\d|\d*\.\d+)\2$!$1\{$3\\textwidth\}!;
+	($pxs,$rwidth) = &convert_length($rwidth);
+	$colspec =~ s/>$content_mark/ ROWSPAN=$rowspan WIDTH=$pxs$&/;
+    }
 
     $text = &styled_text_chunk('SPAN','mrow','','','','', $_);
     $text = &translate_commands($text) if ($text =~ /\\/);
@@ -1335,7 +1335,7 @@
 	    , ''
             , &process_undefined_environment("tex2html_wrap_inline", $id, $saved)
 	    , '' );
-    } else { $_ = join('', $comment, $labels, $_ ) }
+    } else { $_ = join('', $comment, $labels, $_ ); }
     if ($border||($attributes)) {
         &make_table( $border, $attribs, '', '', '', $_ )
     } else { $_ }
diff -Naur latex2html-2018.2.orig/versions/unicode.pl latex2html-2018.2/versions/unicode.pl
--- latex2html-2018.2.orig/versions/unicode.pl	2018-05-16 22:31:33.000000000 +0700
+++ latex2html-2018.2/versions/unicode.pl	2020-11-10 21:42:48.660079644 +0700
@@ -70,9 +70,11 @@
     # MRO: by reference; local(*_) = @_;
     my $char, $uchar;
     return($_[0]) if ($NO_UTF && !$USE_UTF);
-    $_[0] =~ s/([\200-\377])/$char="\".ord($1).";";
+    $_[0] = decode_utf8($_[0]);			# enable unicode matches
+    $_[0] =~ s/([\N{U+0080}-\N{U+FFFF}])/$char="\".ord($1).";";
 	$unicode_table{$char}||$char
     /eg;
+    $_[0] = encode_utf8($_[0]);			# and convert back to UTF
 #	$uchar = $unicode_table{$char};($uchar ? $uchar : $char)/eg;
 }