Attention Conservation Notice:
This note explains my precarious build process for putting a $\LaTeX{}$ paper online using latex2html in 2025.
Your time would probably be better spent writing a paper.
If you want1 to read about a crazy house of cards, which is liable to collapse at any moment, read on.
Recently, I wanted to put the finishing touches on my paper Jayne in Brief.
The publisher doesn’t use LaTeX and wanted graphics of all the images in the paper.
Tools like pandoc totally chocked on the contents of the paper; too many weird custom macros.
The simplest fix seemed to be to convert the paper to a webpage and extract all the images.
latex2html is a LaTeX package used to convert LaTeX documents to HTML.
It was first released in the early 1990s and its most recent release was 2021.
One could say that it has been around for a while.
It is one of the oldest static site generators in active use.
As such, its default output tends to look rather archaic.
Getting the output to work and look reasonable took some doing.
The first issue was that latex2html broke some of the equations.
For example, it produced $@@F$ in place of .
Essentially,
latex2html was outputting internal LaTeX commands instead of actual output.
The relevant bit of LaTeX code defining that symbol is here.
\newcommand*{\overRightarrow}{\mathpalette{\overarrow@\Rightarrowfill@}}
The $@@$ in the broken output is the pair of $@$ symbols at the end there.
I’m sure a LaTeX grey beard could diagnose this problem better.
To fix this, I jerry-rigged a process producing little svgs of the relevant equations.
There were only about a dozen of these, so it seemed fine to fix them one-by-one2.
I made a script fix.sh and a list equations.txt with the following format:
image.svg LaTeX code
Each line of the file contains the name of a broken image to be replaced, as well as the code to replace it. For example,
# Table of Notation
jayne-latex2html/img15.svg \overRightarrow{F}
jayne-latex2html/img16.svg \underLeftarrow{F}
# A Fish Spear
jayne-latex2html/img84.svg \Circled{2}\ \overLeftarrow{R2}(\pickupbelow{Lp}) \NormalPosition :\ \ll R2\Loop
The script fix.sh reads this file line-by-line, ignores comments, and them
compiles the relevant code of each equation in a small LaTeX file with the standalone document class.
It took a bit of fiddling to figure out the correct math delimeters for the standalone class.
In the end, the stripped down mini-document is the following.
\documentclass{standalone}
%% lots of packages and headers
\begin{document}
$\displaystyle
%% PUT EQUATION HERE
$
\end{document}
Running LaTeX on the code above produces lovely little dvis of the relevant equations.
To turn them in to svgs, I used:
dvisvgm --no-fonts --bbox=1 $DVIFILE;
The --bbox=1 here does a little bit of bounding box padding.
Without it, the svg would gets cropped in such a way that the arrows didn’t look quite right.
The script fix.sh runs through the list of equations, makes the relevant
documents, and plots the svgs in to the right places.
It is available here, in all its gory detail.
fix.sh
#!/bin/bash
INPUT_FILE="$1"
COUNT=0;
PWD=$(pwd)
red_message () {
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${RED}$1${NC}"
}
create_tex () {
# Usage create_tex "$FILE" "$EQUATION"
TEXFILE_local=$1;
EQUATION_local=$2;
cat > "$TEXFILE_local" <<'EOF'
\documentclass{standalone}
\usepackage{amsmath,amsfonts,amsthm,amssymb}
\usepackage{extramarks}
\usepackage{latexsym}
\usepackage{circledsteps}
\makeatletter
% cf. amsmath.sty lines 907-920
% \def\leftarrowfill@{\arrowfill@\leftarrow\relbar\relbar}
% \def\rightarrowfill@{\arrowfill@\relbar\relbar\rightarrow}
% \def\leftrightarrowfill@{\arrowfill@\leftarrow\relbar\rightarrow}
% \def\Leftarrowfill@{\arrowfill@\Leftarrow\Relbar\Relbar}
% \def\Rightarrowfill@{\arrowfill@\Relbar\Relbar\Rightarrow}
% \def\Leftrightarrowfill@{\arrowfill@\Leftarrow\Relbar\Rightarrow}
% \def\overarrow@#1#2#3{\vbox{\ialign{##\crcr#1#2\crcr
% \noalign{\nointerlineskip}$\m@th\hfil#2#3\hfil$\crcr}}}
% \ams@renewcommand{\overrightarrow}{%
% \mathpalette{\overarrow@\rightarrowfill@}}
% \ams@renewcommand{\overleftarrow}{%
% \mathpalette{\overarrow@\leftarrowfill@}}
% \ams@newcommand{\overleftrightarrow}{%
%\ams@newcommand{\underrightarrow}{%
% \mathpalette{\underarrow@\rightarrowfill@}}
%\ams@newcommand{\underleftarrow}{%
% \mathpalette{\underarrow@\leftarrowfill@}}
%\ams@newcommand{\underleftrightarrow}{%
% \mathpalette{\underarrow@\leftrightarrowfill@}}
\newcommand*{\overRightarrow}{\mathpalette{\overarrow@\Rightarrowfill@}}
\newcommand*{\overLeftarrow}{\mathpalette{\overarrow@\Leftarrowfill@}}
\newcommand*{\underLeftarrow}{\mathpalette{\underarrow@\Leftarrowfill@}}
\newcommand*{\underRightarrow}{\mathpalette{\underarrow@\Rightarrowfill@}}
\makeatother
\newcommand{\SmallTriangle}{\topinset{s}{\scalebox{1.5}{$\bigtriangleup$}}{5pt}{-0.2pt}}
\newcommand{\NotDistinct}{\topinset{=}{\ensuremath{\mid}}{3pt}{0pt}}
\newcommand{\Extend}{\ensuremath{|}}
\newcommand{\FinalExtend}{\ensuremath{\textrm{I}}}
\newcommand{\NormalPosition}{\ensuremath{\#}}
\newcommand{\Release}{\ensuremath{\Box}}
\newcommand{\Exchange}[1]{X{#1}}
\newcommand{\Opening}[1]{\underline{O}. {#1}}
\newcommand{\Figure}[1]{}
%%\newcommand{\Figure}[1]{\{F.\ {#1}\}}
\newcommand{\pickupbelow}[1]{\underline{#1}}
\newcommand{\pickupabove}[1]{\overline{#1}}
\newcommand{\Loop}{\infty}
\newcommand{\Pindiki}{\mathbb{P}}
\begin{document}
$\displaystyle
EOF
# drop in the equation
echo "$EQUATION_local" >> $TEXFILE_local
# append the file ending
cat >> "$TEXFILE_local" <<'EOF'
$
\end{document}
EOF
}
while read -r line; do
# skip blank or comment lines
[[ -z "$line" || "$line" =~ ^# ]] && continue
COUNT=$((COUNT+1))
SVG_TARGET=$(echo "$line" | awk '{print $1}')
EQUATION=$(echo "$line" | cut -d' ' -f2-)
#echo "path: $svg_path equation: $equation count: $COUNT
WORKING_DIR=$(mktemp -d);
#echo "$WORKING_DIR";
TEXFILE="$WORKING_DIR/equation-$COUNT.tex"
DVIFILE="$WORKING_DIR/equation-$COUNT.dvi"
SVGFILE="$WORKING_DIR/equation-$COUNT.svg"
create_tex $TEXFILE "$EQUATION";
# cat $TEXFILE;
cd $WORKING_DIR;
latex $TEXFILE;
dvisvgm --no-fonts --bbox=1 $DVIFILE;
red_message "created $SVGFILE";
cp -v $SVGFILE $SVG_TARGET
done < "$INPUT_FILE"
Fortunately, the static site produced by latex2html has some CSS styling.
I wanted the web version to line up with my own personal style, so I made a
copy of jayne.css, modified it appropriately, and set it up as the deault
style. The modifications were pretty minor3.
BODY {
font-size: 100%;
font-family: serif;
margin:40px auto;
max-width:650px;
line-height:1.6;
font-size:18px;
color:#444;
background-color: #EEEEEE;
padding:0 10px;
}
The CSS also needed a bit of magic in order to adapt the icons and math font to dark mode, but we’ll get there in a moment.
The deafult navigation icons that ship with latex2html look like this.
(You should really view these icons in light mode.)
These are not especially modern looking icons.
They are, in fact, the png versions of the gif icons that latex2html was using in 1995.
I decided that I ought to modernize the icons a bit.
A quick check on iconify.design revealed a true plethora of arrow symbols.
I decided to go with the material-symbols set formatted as 32x32 pngs.
Initially, I just renamed the files following the same conventions latex2html and dropped them in place.
This gave a grossly misshapen navigation bar because latex2html hardcoded the icon sizing.
<IMG WIDTH="37" HEIGHT="24" ALT="next" SRC="next.png"></A>
<IMG WIDTH="26" HEIGHT="24" ALT="up" SRC="up_g.png">
<IMG WIDTH="63" HEIGHT="24" ALT="previous" SRC="prev_g.png">
To fix4 these hardcoded sizes, I used the following heavy handed sed command
which strips the WIDTH and HEIGHT tags from any line containing a navigation button as a SRC tag.
sed -i -E '/SRC="(next|up|prev)(_g)?\.png"/ s/ ?(WIDTH|HEIGHT)="[0-9]+"//g' ./jayne-latex2html/*.html
However, there was an issue with the dark mode version.
The new icon pngs are black on transparent.
This makes them invisible in darkmode.
A similar issue occurs with the svg files representing math equations.
To make both of these appear similar to the amber on black text in darkmode, I used an image filter5 in CSS.
img[src$=".svg"], img[src$=".png"] {
filter: invert(53%) sepia(90%) saturate(2000%) hue-rotate(10deg);
}
This produces a pretty good amber on black effect. It’s clear that the colours are a bit off, but I’ll take it.
I wanted to include a picture in the article which was not generated by the document itself; that is, a graphic that is neither an icon nor an equation. This turned out to be more cumbersome than I expected. For reasons that are not clear to me, I needed to write in both the LaTeX and HTML code for including the graphic.
\usepackage{html}
\begin{latexonly}
\begin{figure}[h]
\centering
\includegraphics[width=0.9\textwidth]{ASSETS/storer-osage-diamonds-calculus-trans.png}
\label{fig:osage}
\end{figure}
\end{latexonly}
\begin{rawhtml}
<img style="max-width: 100%; height: auto; display: block; margin: 1em 0;" src="storer-osage-diamonds-calculus-trans.png"><BR>
\end{rawhtml}
latex2html Variables
#
There were a handful of things that I wanted to change about the way that latex2html rendered the document.
Initially, the section titles would get cut off in the navigation panel.
This was a quick fix.
# Tweaks to /etc/latex2html.conf
WORDS_IN_NAVIGATION_PANEL_TITLES = -1; # no limit
By default, latex2html names the html files it produces sequentially: node1.html, node2.html, etc.
This feels a bit silly to me.
On reflecion, it is designed that way to guarantee unique file names.
latex2html -info "$ABOUT" -long_titles 10 -dir ./jayne-latex2html/ jayne.tex
The -long_titles 10 option below generates document files using the first ten words of each section title.
In my article, these were definitely all unique.
The command line option -info "$ABOUT" creates an “About this document…” page using the contents of $ABOUT.
I built up a more descriptive $ABOUT string using:
ABOUT="<STRONG>Jayne in Brief</STRONG><P> This document was generated using the <A HREF="http://www.latex2html.org/">LaTeX2HTML</A> translator Version 2021.2 (Released July 1, 2021). The translation was initiated on $(date --iso=second).</P>";
ABOUT+="<P>This document is also available as: <ul><li>A small page PDF file: <a href=\"jayne.pdf\">jayne.pdf</a></li><li>A printable PDF zine: <a href=\"jayne-book.pdf\">jayne-book.pdf</a></li></ul>";
ABOUT+="<P>For details on the how this webpage and article were produced, see this note: <A HREF=\"https://padey.ca/notes/polishing-up-latex2html/\">Polishing Up LaTeX2HTML</A>.</P>"
The last line, which links to this page, is so meta.
The “About this document…” page contains links to a couple different versions of the final article. It is also available as:
To produce these versions, and plop them in the same directory at the web version, was straight forward compared to producing the HTML version.
The zine version is produced by the exellent package pdfbook2.
# make standard version
pdflatex jayne.tex
cp jayne.pdf ./jayne-latex2html/
# make zine version
pdfbook2 --paper=letterpaper jayne.pdf
cp jayne-book.pdf ./jayne-latex2html/
In writing this note, I am so tempted to write a literate program that does all the stuff described above and produces this page.
If I were to do so, I’d probably document it all in the make.sh file which builds the HTML version and pdf version.
Or, rather, I would write a literate program make.sh nestled inside a docstrip program containing all the LaTeX and helper scripts.
In any case, here’s the build script.
I’ll see myself out.
make.sh#!/bin/bash
# compile the LaTeX version
pdflatex jayne.tex
cp jayne.pdf ./jayne-latex2html/
# compile the zine version
pdfbook2 --paper=letterpaper jayne.pdf
cp jayne-book.pdf ./jayne-latex2html/
# compile the latex2html version
ABOUT="<STRONG>Jayne in Brief</STRONG><P> This document was generated using the <A HREF="http://www.latex2html.org/">LaTeX2HTML</A> translator Version 2021.2 (Released July 1, 2021). The translation was initiated on $(date --iso=second).</P>";
ABOUT+="<P>This document is also available as: <ul><li>A small page PDF file: <a href=\"jayne.pdf\">jayne.pdf</a></li><li>A printable PDF zine: <a href=\"jayne-book.pdf\">jayne-book.pdf</a></li></ul>";
ABOUT+="<P>For details on how this webpage and article were produced, see this note: <A HREF=\"https://padey.ca/notes/polishing-up-latex2html/\">Polishing Up LaTeX2HTML</A>.</P>"
latex2html -info "$ABOUT" -long_titles 10 -dir ./jayne-latex2html/ jayne.tex
# add darkmode to the CSS
cp -rv icons-and-css/*.css jayne-latex2html/
# update icons
cp -rv icons-and-css/*.png jayne-latex2html/
# fix the rigid sizing of the icons
sed -i -E '/SRC="(next|up|prev)(_g)?\.png"/ s/ ?(WIDTH|HEIGHT)="[0-9]+"//g' ./jayne-latex2html/*.html
# fix the broken equations
./fix.sh equations.txt
# update local copy
rsync -avz jayne-latex2html /home/pgadey/Hugo/pgadey/static/experimental/
# update web copy
rsync -avz jayne-latex2html cloudbox:/home/pgadey/public_html/pgadey/experimental/
Thanks to my colleague, Paco, for encouraging me to write this. Hopefully, someone benefits from it.
I’m writing this document for myself, in case I ever need to fix this thing. ↩︎
It seems possible to completely automate this process using the
output of latex2html -verbosity 8. This seems to have enough detail to
figure out which equations wouldn’t display right. As there were only a dozen
or so broken equations out of about two hundred, I figured it was fine to go
with the fix.sh solution outline above. ↩︎
I owe these modifications to the clowns over at: }http://bettermotherfuckingwebsite.com/ ↩︎
In principle, one could go and modify some Perl configuration file to fix this. At a first glance, it doesn’t seem to be in /etc/latex2html.conf. It seems that one can customize the navigation panel but it involves writing a new sub navigation_panel in Perl. ↩︎
Thanks ChatGPT, for tweaking the constants to make this work. I have no idea how I would have found them otherwise. ↩︎
Published: Oct 25, 2025 @ 06:47.
Last Modified: Nov 2, 2025 @ 16:30.
Home / Now / Blog / Notes / Reading / Office Camera / Tags / Bookmarks / RSS Feeds / Top of Page
Thanks for reading! If you have any comments or questions about the content, please let me know. Anyone can contact me by email.