Building a Website with Make4ht: Raster and Vector Images
Introduction
With basic text, references and footnotes, equations, tables, and code listings all implemented, the last major form of content to set up for this website is graphics. Images are the most ubiquitous form of content on the internet after text, thus requiring that they be handled well and robustly. Fortunately, this is remarkably easy when simply inserting a typical raster1Wikipedia: Raster Graphics. https://en.wikipedia.org/wiki/Raster_graphics. Accessed: 2026-03-29. or vector2Wikipedia: Vector Graphics. https://en.wikipedia.org/wiki/Vector_graphics. Accessed: 2026-03-29. graphic from an external file into the document. Indeed, the standard LaTeX tools work without any problems. However, the TIKZ and PGFplots packages for LaTeX provide powerful drawing and plotting tools which will take some small effort to properly implement.
Raster Images
Raster graphics are commonly used throughout both the internet and documents typically produced by LaTeX such as scientific publications. Hence, there is already robust support for them in place in both LuaTeX and Make4ht. Figure 1 demonstrates that even very small raster images can be cleanly displayed. Juxtapose this to Figure 1 of the first article in this series, a 1080×1080 pixel image which is also handled without issue.

Though it is not strictly necessary, the system presented here does make a small modification to the standard \includegraphics
command in the configuration file such that the output of Make4ht is somewhat cleaner. This can be found in
Listing 1.
Vector Images
Vector images are rather less common, but by no means unknown to either LaTeX or HTML. Indeed, the most
common vector format, SVG, is directly embeddable into HTML documents3MDN Web Docs: SVG in HTML. https://developer.mozilla.org/en-US/docs/Web/SVG/Guides/SVG_in_HTML. Accessed: 2026-03-29. as they share a common XML file
structure. Unfortunately, the SVG format is not natively supported by LaTeX, and thus the SVG package4CTAN: SVG. https://ctan.org/pkg/svg. Accessed: 2026-03-29. must be
used to include an external SVG into the document. Fortunately, the interface, \includesvg, is extremely similar to the
normal LaTeX command for including graphics. Thus far, there has not been any need to modify or customize the
process for the inclusion of external SVG files. Figure 2 provides an example of the inclusion of an external SVG
file.
\renewcommand{\includegraphics}[2][]{
\HCode{
<div class="figure">
<img alt="PIC" src="#2">
</div>
}
}
TIKZ and PGFplots
TIKZ5TIKZ Manual. https://tikz.dev. Accessed: 2026-04-06. and the related PGFplots6PGFplots Manual. https://tikz.dev/pgfplots. Accessed: 2026-04-06. packages provide some of the most sophisticated diagram and plot generation capabilities for LaTeX. Typically, one would simply create their image as normal and allow Make4ht to create an independent PDF of it that would then be embedded into the HTML document like any other. However, one of the goals for this website is to ensure a clear and consistent experience across both light and dark modes. This would be lost if vector images were stuck with a white background or, worse, had a transparent background coupled with dark lines that would be lost on a dark background.
Fortunately, there is a way to work around this by ensuring that vector images created using TIKZ and PGFplots are embedded directly into the HTML document such that CSS styling can be used to modify the colors displayed in the embedded SVGs. The manner in which to do this is described starting with Listing 2 which provides a small addition to the HTMLArticle class file defining a command through which all TIKZ and PGFplots images must be made. Note that this requires that each image be defined in its own file, but this is good practice regardless.
\newcommand{\inputtikz}[1]{
\tikzsetnextfilename{Figure_\thefigure}
\input{#1}
}
The code in Listing 2 also requires that the externalize package for TIKZ be used. This ensures that independent PDF files are created for each TIKZ image. These PDF files are then converted to SVG images using Inkscape7Inkscape. https://inkscape.org/. Accessed: 2026-04-06. when running Make4ht as defined in the configuration file with the addition of the code in Listing 3 to it.
\renewcommand{\inputtikz}[1]{
\HCode{<div class="figure">
<stub class="tikz" file="figures/Figure_\thefigure.svg">
</stub></div>}
\ShellEscape{inkscape --export-type=svg --export-plain-svg \
--pdf-font-strategy=keep figures/Figure_\thefigure.pdf}
}
Before progressing to the post-processing phase, there is the matter of how precisely to define which colors will be substituted when swapping between color modes on the website. Listing 4 provides the definition of a number of commonly used colors across both the PDF and light-mode HTML versions of these documents. More accurately, it defines colors which are imperceptibly different from the actual target values such that the actual colors themselves may be used without worry of them being altered during a color mode change if the need should arise. At present, the only colors defined are those typically used for the text, background, borders, and accents in addition to two data series color sets, one in grey scale and the other using color palette but forward by Okabe and Ito8Color Universal Design (CUD) - How to make figures and presentations that are friendly to Colorblind people -. https://jfly.uni-koeln.de/color/index.html. Accessed: 2026-04-12. .
% Basic colors for foreground/background/border
\definecolor{textcolor}{RGB}{1, 1, 1}
\definecolor{backgroundcolor}{RGB}{254,254,254}
\definecolor{bordercolor}{RGB}{134, 135, 136}
\definecolor{primaryaccentcolor}{RGB}{182, 183, 184}
\definecolor{secondaryaccentcolor}{RGB}{86, 87, 88}
% Data series colors: Grey scale
\definecolor{dataBW0}{RGB}{ 61, 61, 61}
\definecolor{dataBW1}{RGB}{112, 112, 112}
\definecolor{dataBW2}{RGB}{163, 163, 163}
\definecolor{dataBW3}{RGB}{214, 214, 214}
% Data series colors: Okabe and Ito
\definecolor{dataOAI0}{RGB}{ 2, 2, 2}
\definecolor{dataOAI1}{RGB}{ 2, 158, 115}
\definecolor{dataOAI2}{RGB}{ 2, 114, 178}
\definecolor{dataOAI3}{RGB}{ 86, 180, 233}
\definecolor{dataOAI4}{RGB}{220, 208, 46}
\definecolor{dataOAI5}{RGB}{230, 159, 2}
\definecolor{dataOAI6}{RGB}{213, 94, 2}
\definecolor{dataOAI7}{RGB}{204, 121, 167}
#Factor by which to scale SVG images during the processing
svgScaleFactor = 4.0 / 3.0
#list of svgColor / class tuples to allow for dynamic css styling
svgColorMap = [
("#010101", "text"),
("#fefefe", "background"),
("#b6b7b8", "primary-accent"),
("#565758", "secondary-accent"),
("#3d3d3d", "data-series-bw-0"),
("#707070", "data-series-bw-1"),
("#a3a3a3", "data-series-bw-2"),
("#d6d6d6", "data-series-bw-3"),
("#020202", "data-series-oai-0"),
("#029e73", "data-series-oai-1"),
("#0272b2", "data-series-oai-2"),
("#56b4e9", "data-series-oai-3"),
("#dcd02e", "data-series-oai-4"),
("#e69f02", "data-series-oai-5"),
("#d55e02", "data-series-oai-6"),
("#cc79a7", "data-series-oai-7")
]
# Helper function for the replacement of the initial width and height of an svg
def replaceDimension(match):
out = str(svgScaleFactor * float(match.group(2)))
return match.group(1) + "=\"" + out + "\""
#
# Helper function for the replacment of colors in the insertTIKZImages function
def replaceColors(match):
out = match.group(2)
for map in svgColorMap:
if match.group(2) == map[0]:
out = "var(--" + map[1] + ")"
break
#
#
return match.group(1) + ":" + out
#
# Find all tikz images that need to be replaced and perform the substitution
def insertTIKZImages():
images = findTags(0, "stub", [("class", "tikz")])
for image in images:
fileName = ""
for attr in nodes[image].value[1]:
if[attr[0] == "file"]:
fileName = attr[1]
#
#
imageFile = open(fileName, "rt")
imageText = imageFile.read()
imageFile.close()
# Regex Operations:
## Scale graphic by svgScaleFactor
imageText = re.sub("(width)=\"(\d+?\.\d+|\d+)\"",
replaceDimension,
imageText,
1)
imageText = re.sub("(height)=\"(\d+?\.\d+|\d+)\"",
replaceDimension,
imageText,
1)
## Substitute colors for reactive content controlled by CSS
imageText = re.sub("(fill|stroke):(#\w{6})", replaceColors, imageText)
## Remove font-family attributes
imageText = re.sub("font-family:.+?;", "", imageText)
nodes.append(TreeNode(HTMLContentType.CONTENT,
imageText,
nodes[image].parent,
[]))
imageIndex = nodes[nodes[image].parent].children.index(image)
nodes[nodes[image].parent].children[imageIndex] = len(nodes) - 1
#
#
The color definitions (specifically their hexcode representations) are sought out in the post-processing step as
provided by Listing 5. Here, each of the colors defined in Listing 4 are replaced with CSS var() functions which
reference variables that define the color currently in use for that part of the document. Discussion of defining the
colors for the light and dark modes of the website is reserved for the next article. The post-processing also scales
the images in the HTML version of the document up by about 33% and removes any font-family
information such that the font used in the images will be the same as that used for the rest of the
website.
Summary
Overall, the process of getting images to be correctly translated from the PDF files to the HTML documents is generally straightforward, at least in comparison to getting tables to a functional state. Even the special case of TIKZ and PGFplot images is fairly simple, though it does require the use of an unusually large number of specific tools such the externalize package, Inkscape, and some very specific CSS. On that note, it is important to keep in mind that the system as it is presented here relies on the close coordination of the colors defined in Listing 4, the substitution table of hexcode-CSS variable tuples in Listing 5, and the CSS variables as defined in the style file. Any mismatch between these three may result in broken behavior.
Before progressing to the next article which is dedicated to the CSS styling of the website, readers may wish to take a moment and examine the examples below. They were all generated with TIKZ and/or PGFplots and showcase a mere fraction of the capabilities of the two packages. Indeed, Figures 2 and 3 of the first article in this series also demonstrate the use of the packages.
Examples
References
- Wikipedia: Raster Graphics. https://en.wikipedia.org/wiki/Raster_graphics. Accessed: 2026-03-29.
- Wikipedia: Vector Graphics. https://en.wikipedia.org/wiki/Vector_graphics. Accessed: 2026-03-29.
- MDN Web Docs: SVG in HTML. https://developer.mozilla.org/en-US/docs/Web/SVG/Guides/SVG_in_HTML. Accessed: 2026-03-29.
- CTAN: SVG. https://ctan.org/pkg/svg. Accessed: 2026-03-29.
- TIKZ Manual. https://tikz.dev. Accessed: 2026-04-06.
- PGFplots Manual. https://tikz.dev/pgfplots. Accessed: 2026-04-06.
- Inkscape. https://inkscape.org/. Accessed: 2026-04-06.
- Color Universal Design (CUD) - How to make figures and presentations that are friendly to Colorblind people -. https://jfly.uni-koeln.de/color/index.html. Accessed: 2026-04-12.
- Wikipedia: Diode Bridge. https://en.wikipedia.org/wiki/Diode_bridge. Accessed: 2026-04-11.
- Wikipedia: Normal Distribution. https://en.wikipedia.org/wiki/Normal_distribution. Accessed: 2026-04-11.
- Wikipedia: Fermat's Spiral. https://en.wikipedia.org/wiki/Fermat%27s_spiral. Accessed: 2026-04-11.
- H. Vogel. A better way to construct the sunflower head. Mathematical Biosciences, 44(3):179-189, 1979.