As I use tex4ht/make4ht more, I find I'm building up a collection of my own customisations. To help organise this, is there a recommended way to manage including (multiple) custom filters/domfilters within make4ht?

I know that (specifically for a domfilter) I can make a file build.lua with

local domfilter = require "make4ht-domfilter"
local process = domfilter {
  -- do stuff here
  return dom
Make:match("html$", process)

and then run make4ht -e build.lua.

But what if I want to have a few different (dom)filters for doing different things? Can I split them out into different files and specify somehow which to run?

Also, is it possible to specify (custom or 'standard') filters to run from within a .sty/.4ht package, or within the .cfg? (I recognise that this could pose a security risk...)

Lots of questions here I'm afraid, but answers to any would be very welcome.

1 Answer 1


It is unfortunately not possible to modify build sequence from the TeX side, everything needs to be done in Lua files.

You can easily define new DOM filter modules. You just need to use the following name scheme: domfilters/make4ht-<filtername>.lua. The domfilters directory can be placed in your current directory, but if you want to make it universally available, it is best to place it in your local TEXMF tree, in the scripts/lua/ directory. For example on my system, it can be ~/texmf/script/lua/domfilters or ~/texmf/script/lua/make4ht/domfilters.

DOM filter module should export function that takes the DOM object, makes modifications to it and then return it. See existing DOM filters for inspiration.

Sample DOM filter then can look like this (named ~/texmf/scripts/lua/make4ht/domfilters/make4ht-test.lua):

return function(dom)
  print "Hello world"
  return dom

You can require this DOM filter from a build file like this:

local domfilters = require "make4ht-domfilter"

local process = domfilters {"test"}
Make:match("html$", process)

You can chain multiple filters:

local process = domfilters {"test", "fixinlines"}

Each step takes DOM object from the previous filter, so you don't need to parse HTML file again.

If you want to make your build sequence available in multiple projects, you have two choices:

  1. make4ht configuration file
  2. new extension

make4ht configuration file is something different than than TeX4ht configuration file (TeX file with .cfg extension). It is special Lua build file that can be placed in your home directory or a project root. It should be named .make4ht. So you can just take the build file example from the above, rename it to .make4ht and put it in any of parent directories of your project and it will be used automatically.

Extensions can modify various aspects of the build process. They should be placed in make4ht/extensions directory and named as make4ht-ext-<filename>.lua. They need to export table with functions, most notably modify_build.

Here is a sample extension make4ht-ext-sample.lua:

local M = {}

local filter = require "make4ht-domfilter"

function M.test(format)
  print("hello format", format)
  return true

function M.modify_build(make)
  -- number of filters that should be moved to the beginning
  print "hello modify build"
  local process = filter {"test"}
  make:match("html?$", process)
  return make

return M

Function M.test takes current output format and decides if the extension should be executed or not. Some extension support only HTML for example, so this function then returns false and it is not used at all.

Function M.modify_build can contain similar code as build files, but make4ht variable, passed as a parameter, is used instead of the global Make4ht object that is used in the ordinary build files.

Extension can be required using the f parameter of make4ht, like

make4ht -f html5+sample filename.tex

Output of this command is:

[STATUS]  make4ht: Conversion started
[STATUS]  make4ht: Input file: sample.tex
hello format    html5
hello modify build
Hello world
[STATUS]  make4ht: Conversion finished
  • Thanks for this very nice explanation. Two questions. 1: In your extension version you have make:match("html?$", process) – does the ? do something there? What is the match matching against, filenames? 2: It seems that when I use the make4ht config, my filters get run before common_domfilters (if using -f html5, that is), whereas if I use the extension, they run after. What determines the order of processing? Commented Sep 24, 2020 at 18:03
  • @AlexWatson yes, names of output files are used. the ? is used because one user uses .htm extension, so this regular expression matches both .html and .htm.
    – michal.h21
    Commented Sep 24, 2020 at 18:14
  • @AlexWatson config file is processed before extensions are registered, so filters that you register here have priority. note that extensions can modify the build files and some of them insert their filters at the beginning of the build sequence. extensions required from the command line are registered after default extensions, so they are always processed after them.
    – michal.h21
    Commented Sep 24, 2020 at 18:17
  • OK - so just to check I understood, when domfilter{...} is run, ... is put into a table of domfilters to be run at the end -- probably in order, but maybe not if an extension messes with it by hand? Presumably all this is done at the end, after the htm(l) file is made by tex4ht? Commented Sep 24, 2020 at 19:55
  • 1
    @AlexWatson yes, filters are normally put to build sequence table in the order when they are requested. build sequence is created before compilation starts, because extensions can modify not only filters, but they also can add new commands to be executed (tidy, for example).
    – michal.h21
    Commented Sep 25, 2020 at 8:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .