texlive[70097] trunk: make4ht (23feb24)

commits+karl at tug.org commits+karl at tug.org
Fri Feb 23 23:03:29 CET 2024


Revision: 70097
          https://tug.org/svn/texlive?view=revision&revision=70097
Author:   karl
Date:     2024-02-23 23:03:29 +0100 (Fri, 23 Feb 2024)
Log Message:
-----------
make4ht (23feb24)

Modified Paths:
--------------
    trunk/Build/source/texk/texlive/linked_scripts/make4ht/make4ht
    trunk/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
    trunk/Master/texmf-dist/doc/support/make4ht/README
    trunk/Master/texmf-dist/doc/support/make4ht/changelog.tex
    trunk/Master/texmf-dist/doc/support/make4ht/make4ht-doc.pdf
    trunk/Master/texmf-dist/doc/support/make4ht/readme.tex
    trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua
    trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-idcolons.lua
    trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-inlinecss.lua
    trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-mathmlfixes.lua
    trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-sectionid.lua
    trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-tablerows.lua
    trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-dvisvgm_hashes.lua
    trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-latexmk_build.lua
    trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-staticsite.lua
    trunk/Master/texmf-dist/scripts/make4ht/filters/make4ht-domfilter.lua
    trunk/Master/texmf-dist/scripts/make4ht/formats/make4ht-jats.lua
    trunk/Master/texmf-dist/scripts/make4ht/formats/make4ht-odt.lua
    trunk/Master/texmf-dist/scripts/make4ht/make4ht
    trunk/Master/texmf-dist/scripts/make4ht/make4ht-errorlogparser.lua
    trunk/Master/texmf-dist/scripts/make4ht/make4ht-htlatex.lua
    trunk/Master/texmf-dist/scripts/make4ht/make4ht-indexing.lua
    trunk/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua
    trunk/Master/texmf-dist/scripts/make4ht/mkparams.lua
    trunk/Master/texmf-dist/scripts/make4ht/mkutils.lua

Added Paths:
-----------
    trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-copy_images.lua

Modified: trunk/Build/source/texk/texlive/linked_scripts/make4ht/make4ht
===================================================================
--- trunk/Build/source/texk/texlive/linked_scripts/make4ht/make4ht	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Build/source/texk/texlive/linked_scripts/make4ht/make4ht	2024-02-23 22:03:29 UTC (rev 70097)
@@ -29,7 +29,7 @@
 
 -- set version number. the template should be replaced by the
 -- actual version number by the build script
-local version = "v0.3m"
+local version = "v0.4"
 mkparams.version_number = version
 
 local args = mkparams.get_args()
@@ -39,6 +39,11 @@
 log:status("Conversion started")
 log:status("Input file: " .. parameters.tex_file)
 
+
+if parameters.builddir and parameters.builddir ~= "" then
+  mkutils.make_path(parameters.builddir)
+end
+
 local mode = parameters.mode
 local build_file = parameters.build_file 
 
@@ -127,9 +132,10 @@
     log:info("No output directory")
     return true
   end
-	log:info("outdir: "..outdir)
-	local outfilename = outdir .. filename
-	mkutils.copy(filename,outfilename)
+  log:info("outdir: "..outdir)
+  local outfilename = filename:gsub("^" .. (par.builddir or ""), "")
+  outfilename = outdir .. outfilename
+  mkutils.copy(filename,outfilename)
 	return true
 end)
 

Modified: trunk/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
===================================================================
--- trunk/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl	2024-02-23 22:03:29 UTC (rev 70097)
@@ -1,5 +1,5 @@
 #!/usr/bin/env perl
-# $Id: tlmgr.pl 70001 2024-02-19 23:17:07Z karl $
+# $Id: tlmgr.pl 70080 2024-02-22 23:13:07Z karl $
 # Copyright 2008-2024 Norbert Preining
 # This file is licensed under the GNU General Public License version 2
 # or any later version.
@@ -8,8 +8,8 @@
 
 use strict; use warnings;
 
-my $svnrev = '$Revision: 70001 $';
-my $datrev = '$Date: 2024-02-20 00:17:07 +0100 (Tue, 20 Feb 2024) $';
+my $svnrev = '$Revision: 70080 $';
+my $datrev = '$Date: 2024-02-23 00:13:07 +0100 (Fri, 23 Feb 2024) $';
 my $tlmgrrevision;
 my $tlmgrversion;
 my $prg;
@@ -8451,7 +8451,7 @@
 as any substring, and outputs bug-reporting and other information for
 the package selected from the results.
 
-The search is equivalent to C<tlmgr search --word --file I<search-string>.
+The search is equivalent to C<tlmgr search --word --file> I<search-string>.
 Thus, I<search-string> is interpreted as a (Perl) regular expression.
 
 =head2 candidates I<pkg>
@@ -10552,7 +10552,7 @@
 distribution (L<https://tug.org/texlive>) and both are licensed under the
 GNU General Public License Version 2 or later.
 
-$Id: tlmgr.pl 70001 2024-02-19 23:17:07Z karl $
+$Id: tlmgr.pl 70080 2024-02-22 23:13:07Z karl $
 =cut
 
 # test HTML version: pod2html --cachedir=/tmp tlmgr.pl >/tmp/tlmgr.html

Modified: trunk/Master/texmf-dist/doc/support/make4ht/README
===================================================================
--- trunk/Master/texmf-dist/doc/support/make4ht/README	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/doc/support/make4ht/README	2024-02-23 22:03:29 UTC (rev 70097)
@@ -34,6 +34,7 @@
          possible values: tex4ht or lua4ht
     -c,--config (default xhtml) Custom config file
     -d,--output-dir (default "")  Output directory
+    -B,--build-dir (default nil)  Build directory
     -e,--build-file (default nil)  If the build filename is different 
          than `filename`.mk4
     -f,--format  (default nil)  Output file format
@@ -207,6 +208,12 @@
 
     $ make4ht -d outputdir filename.tex
 
+`make4ht` can also output temporary files to a build directory, thanks to the `--build-dir` (or `-B`)
+option. The following command with put `.aux`, `.4tc` and other auxiliary files to the
+`build` dir, and the generated `.html` and `.css` files to the `outputdir` directory.
+
+    $ make4ht -B build -d outputdir filename.tex
+
 ## Image conversion and postprocessing of the generated files
 
 \TeX4ht\ can convert parts of the document to images. This is useful 
@@ -248,8 +255,14 @@
 The extensions can be enabled or disabled by appending `+EXTENSION` or `-EXTENSION` after
 the output format name:
 
-     $ make4ht -uf html5+tidy filename.tex
+     $ make4ht -f html5+tidy filename.tex
 
+In `xhtml` and `html5` output formats, the `common_domfilters` extension is triggered automatically, but
+it can still be disabled using:
+
+     $ make4ht -f html5-common_domfilters filename.tex
+
+
 Available extensions:
 
 
@@ -260,9 +273,15 @@
 common\_domfilters
 
 :    clean the HTML file using DOM filters. It is more powerful than
-`common_filters`. Used DOM filters are `fixinlines`, `idcolons`,
-`joincharacters`, `sectionid` and `tablerows`.
+     `common_filters`. It used following DOM filters: `fixinlines`, `idcolons`,
+     `joincharacters`, `mathmlfixes`, `tablerows`,`booktabs`, `sectionid`
+     and`itemparagraphs`
 
+copy\_images
+
+:    Copies the images to the output directory. This is useful if the original
+     images are stored in directories above the document directory.
+
 detect\_engine
 
 :    detect engine and format necessary for the document compilation from the
@@ -1015,6 +1034,24 @@
       }
     }
 
+## The `copy_images` extension
+
+extensions
+
+:  table with list of image extensions that should be processed. 
+
+
+img\_dir
+
+:  name of the output directory where images should be stored
+
+Default values:
+
+     filter_settings "copy_images" {
+        extensions = {"png", "jpg", "jpeg", "svg"},
+        img_dir = ""
+     }
+
 ## The `fixinlines` dom filter 
 
 inline\_elements

Modified: trunk/Master/texmf-dist/doc/support/make4ht/changelog.tex
===================================================================
--- trunk/Master/texmf-dist/doc/support/make4ht/changelog.tex	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/doc/support/make4ht/changelog.tex	2024-02-23 22:03:29 UTC (rev 70097)
@@ -3,6 +3,217 @@
 
 \begin{itemize}
 \item
+  2024/02/22
+
+  \begin{itemize}
+  \tightlist
+  \item
+    version \texttt{0.4} released
+  \item
+    fixed name of the \texttt{\textless{}title-group\textgreater{}}
+    element in JATS output.
+  \end{itemize}
+\item
+  2024/02/21
+
+  \begin{itemize}
+  \tightlist
+  \item
+    add standalone minus sign to the
+    \texttt{\textless{}mn\textgreater{}} element in MathML.
+  \end{itemize}
+\item
+  2024/01/26
+
+  \begin{itemize}
+  \tightlist
+  \item
+    fixed passing of the params table in the \texttt{staticsite}
+    extension.
+  \end{itemize}
+\item
+  2024/01/22
+
+  \begin{itemize}
+  \tightlist
+  \item
+    don't enable parsing of void elements in DOM filter with XML output
+    formats.
+  \end{itemize}
+\item
+  2023/12/15
+
+  \begin{itemize}
+  \tightlist
+  \item
+    call \texttt{fix\_rel\_mo} MathML fix only in the ODT output.
+  \end{itemize}
+\item
+  2023/12/11
+
+  \begin{itemize}
+  \tightlist
+  \item
+    fixed handling of the \texttt{-\/-build-dir} argument in
+    \texttt{dvisvgm\_hashes} extension.
+  \end{itemize}
+\item
+  2023/11/03
+
+  \begin{itemize}
+  \tightlist
+  \item
+    remove leading dashes in ids created by the \texttt{sectionid} DOM
+    filter.
+  \end{itemize}
+\item
+  2023/10/26
+
+  \begin{itemize}
+  \tightlist
+  \item
+    check that removed elements in the \texttt{sectionid} DOM filter are
+    \texttt{\textless{}a\textgreater{}} elements.
+  \end{itemize}
+\item
+  2023/10/25
+
+  \begin{itemize}
+  \tightlist
+  \item
+    fixed addition of named IDs to figures.
+  \end{itemize}
+\item
+  2023/10/19
+
+  \begin{itemize}
+  \tightlist
+  \item
+    print info about packages with no corresponding \texttt{.4ht} file.
+  \end{itemize}
+\item
+  2023/10/05
+
+  \begin{itemize}
+  \tightlist
+  \item
+    added fix for LibreOffice's bug regarding relation type math
+    operators.
+  \end{itemize}
+\item
+  2023/10/03
+
+  \begin{itemize}
+  \tightlist
+  \item
+    fixed most features that didn't work with the \texttt{-\/-build-dir}
+    argument.
+  \end{itemize}
+\item
+  2023/10/02
+
+  \begin{itemize}
+  \tightlist
+  \item
+    working on new command line argument \texttt{-\/-build-dir}. It
+    should allow moving of temporary files to a temporary directory. The
+    idea and most of the code comes from Robin Seth Ekman.
+  \end{itemize}
+\item
+  2023/09/07
+
+  \begin{itemize}
+  \tightlist
+  \item
+    fix spacing for the \texttt{dcases*} environment in the
+    \texttt{mathml\_fixes} DOM filter.
+  \end{itemize}
+\item
+  2023/08/24
+
+  \begin{itemize}
+  \tightlist
+  \item
+    support non-numerical values in index entry destinations (for
+    example roman numerals).
+  \end{itemize}
+\item
+  2023/08/22
+
+  \begin{itemize}
+  \tightlist
+  \item
+    updated list of DOM filters used by the \texttt{common\_domfilters}
+    extension, and documented that it is used automatically in the HTML
+    output.
+  \end{itemize}
+\item
+  2023/08/12
+
+  \begin{itemize}
+  \tightlist
+  \item
+    remove unnecessary \texttt{\textless{}a\textgreater{}} elements with
+    \texttt{id} attributes and set the \texttt{id} on the parent.
+  \end{itemize}
+\item
+  2023/07/06
+
+  \begin{itemize}
+  \tightlist
+  \item
+    add prefix to section IDs if the section name is empty.
+  \end{itemize}
+\item
+  2023/06/20
+
+  \begin{itemize}
+  \tightlist
+  \item
+    added the \texttt{copy\_images} extension.
+  \end{itemize}
+\item
+  2023/06/14
+
+  \begin{itemize}
+  \tightlist
+  \item
+    fixed bug in the \texttt{mathmlfixes} DOM filter -- non-empty last
+    rows were removed if they contained only one element.
+  \end{itemize}
+\item
+  2023/05/26
+
+  \begin{itemize}
+  \tightlist
+  \item
+    load \texttt{tex4ht.sty} before input file processing starts.
+  \end{itemize}
+\item
+  2023/04/19
+
+  \begin{itemize}
+  \tightlist
+  \item
+    handle additional characters in the \texttt{idcolons} DOM filter.
+  \end{itemize}
+\item
+  2023/04/06
+
+  \begin{itemize}
+  \tightlist
+  \item
+    fixed handling of ID attributes in the \texttt{idcolons} DOM filter.
+  \end{itemize}
+\item
+  2023/03/07
+
+  \begin{itemize}
+  \tightlist
+  \item
+    remove empty rows in \texttt{longtable}.
+  \end{itemize}
+\item
   2023/02/24
 
   \begin{itemize}

Modified: trunk/Master/texmf-dist/doc/support/make4ht/make4ht-doc.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/support/make4ht/readme.tex
===================================================================
--- trunk/Master/texmf-dist/doc/support/make4ht/readme.tex	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/doc/support/make4ht/readme.tex	2024-02-23 22:03:29 UTC (rev 70097)
@@ -45,6 +45,7 @@
      possible values: tex4ht or lua4ht
 -c,--config (default xhtml) Custom config file
 -d,--output-dir (default "")  Output directory
+-B,--build-dir (default nil)  Build directory
 -e,--build-file (default nil)  If the build filename is different 
      than `filename`.mk4
 -f,--format  (default nil)  Output file format
@@ -274,6 +275,17 @@
 $ make4ht -d outputdir filename.tex
 \end{verbatim}
 
+\texttt{make4ht} can also output temporary files to a build directory,
+thanks to the \texttt{-\/-build-dir} (or \texttt{-B}) option. The
+following command with put \texttt{.aux}, \texttt{.4tc} and other
+auxiliary files to the \texttt{build} dir, and the generated
+\texttt{.html} and \texttt{.css} files to the \texttt{outputdir}
+directory.
+
+\begin{verbatim}
+$ make4ht -B build -d outputdir filename.tex
+\end{verbatim}
+
 \hypertarget{image-conversion-and-postprocessing-of-the-generated-files}{%
 \subsection{Image conversion and postprocessing of the generated
 files}\label{image-conversion-and-postprocessing-of-the-generated-files}}
@@ -332,9 +344,17 @@
 \texttt{+EXTENSION} or \texttt{-EXTENSION} after the output format name:
 
 \begin{verbatim}
- $ make4ht -uf html5+tidy filename.tex
+ $ make4ht -f html5+tidy filename.tex
 \end{verbatim}
 
+In \texttt{xhtml} and \texttt{html5} output formats, the
+\texttt{common\_domfilters} extension is triggered automatically, but it
+can still be disabled using:
+
+\begin{verbatim}
+ $ make4ht -f html5-common_domfilters filename.tex
+\end{verbatim}
+
 Available extensions:
 
 \begin{description}
@@ -342,9 +362,13 @@
 clean the output HTML files using filters.
 \item[common\_domfilters]
 clean the HTML file using DOM filters. It is more powerful than
-\texttt{common\_filters}. Used DOM filters are \texttt{fixinlines},
-\texttt{idcolons}, \texttt{joincharacters}, \texttt{sectionid} and
-\texttt{tablerows}.
+\texttt{common\_filters}. It used following DOM filters:
+\texttt{fixinlines}, \texttt{idcolons}, \texttt{joincharacters},
+\texttt{mathmlfixes}, \texttt{tablerows},\texttt{booktabs},
+\texttt{sectionid} and\texttt{itemparagraphs}
+\item[copy\_images]
+Copies the images to the output directory. This is useful if the
+original images are stored in directories above the document directory.
 \item[detect\_engine]
 detect engine and format necessary for the document compilation from the
 magic comments supported by \LaTeX~editors such as TeXShop or TeXWorks.
@@ -1097,6 +1121,26 @@
 }
 \end{verbatim}
 
+\hypertarget{the-copy_images-extension}{%
+\subsection{\texorpdfstring{The \texttt{copy\_images}
+extension}{The copy\_images extension}}\label{the-copy_images-extension}}
+
+\begin{description}
+\item[extensions]
+table with list of image extensions that should be processed.
+\item[img\_dir]
+name of the output directory where images should be stored
+\end{description}
+
+Default values:
+
+\begin{verbatim}
+ filter_settings "copy_images" {
+    extensions = {"png", "jpg", "jpeg", "svg"},
+    img_dir = ""
+ }
+\end{verbatim}
+
 \hypertarget{the-fixinlines-dom-filter}{%
 \subsection{\texorpdfstring{The \texttt{fixinlines} dom
 filter}{The fixinlines dom filter}}\label{the-fixinlines-dom-filter}}

Modified: trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-collapsetoc.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -168,7 +168,7 @@
 -- process the .4tc file and convert entries to a tree structure
 -- based on the sectioning level
 local function parse_4tc(parameters, toc_levels)
-  local tcfilename = parameters.input .. ".4tc"
+  local tcfilename = mkutils.file_in_builddir(parameters.input .. ".4tc", parameters)
   if not mkutils.file_exists(tcfilename) then 
     log:warning("Cannot find TOC: " .. tcfilename)
     return {}

Modified: trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-idcolons.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-idcolons.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-idcolons.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -1,18 +1,36 @@
+local allowed_chars = {
+  ["-"] = true,
+  ["."] = true
+}
+local function fix_colons(id)
+  -- match every non alphanum character
+  return id:gsub("[%W]", function(s)
+    -- some characters are allowed, we don't need to replace them
+    if allowed_chars[s] then return s end
+    -- in other cases, replace with underscore
+    return "_"
+  end)
+end
+
 local function id_colons(obj)
-  -- replace : characters in links and ids with unserscores
+  -- replace non-valid characters in links and ids with underscores
   obj:traverse_elements(function(el) 
     local name = string.lower(obj:get_element_name(el))
     if name == "a" then
-      local href = obj:get_attribute(el, "href")
+      local href = el:get_attribute("href")
+      -- don't replace colons in external links
       if href and not href:match("[a-z]%://") then
-        obj:set_attribute(el, "href", href:gsub(":", "_"))
+        local base, id = href:match("(.*)%#(.*)")
+        if base and id then
+          id = fix_colons(id)
+          el:set_attribute("href", base .. "#" .. id)
+        end
       end
     end
-    local id  = obj:get_attribute( el , "id")
+    local id  = el:get_attribute("id")
     if id then
-      obj:set_attribute(el, "id", id:gsub(":", "_"))
+      el:set_attribute("id", fix_colons(id))
     end
-    -- local id = obj:get_attribute(el, "id")
   end)
   return obj
 end

Modified: trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-inlinecss.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-inlinecss.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-inlinecss.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -58,7 +58,7 @@
   if not processed then 
     -- process the CSS file before everything else, but only once
     processed = true
-    local css_file = par.input .. ".css"
+    local css_file = mkutils.file_in_builddir(par.input .. ".css", par)
     local status, msg = parse_css(css_file)
     if not status then log:warning(msg) end
   end

Modified: trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-mathmlfixes.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-mathmlfixes.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-mathmlfixes.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -35,10 +35,39 @@
   el._name = newname
 end
 
-local function create_element(el, name, prefix)
-  return el:create_element(newname)
+local function create_element(el, name, prefix, attributes)
+  local attributes = attributes or {}
+  local newname = get_new_element_name(name, prefix)
+  return el:create_element(newname, attributes)
 end
 
+local function element_pos(el)
+  local pos, count = 0, 0
+  for _, node in ipairs(el:get_siblings()) do
+    if node:is_element() then
+      count = count + 1
+      if node == el then
+        pos = count
+      end
+    end
+  end
+  return pos, count
+end
+
+-- test if element is the first element in the current element list
+local function is_first_element(el)
+  local pos, count = element_pos(el)
+  return pos == 1 
+end
+
+-- test if element is the last element in the current element list
+local function is_last_element(el)
+  local pos, count = element_pos(el)
+  return pos == count
+end
+
+
+
 local function is_token_element(el)
   local name, prefix = get_element_name(el)
   return token_elements[name], prefix
@@ -275,6 +304,14 @@
 local function fix_numbers(el)
   -- convert <mn>1</mn><mo>.</mo><mn>3</mn> to <mn>1.3</mn>
   if get_element_name(el) == "mn" then
+    -- sometimes minus sign can be outside <mn>
+    local x = el:get_sibling_node(-1)
+    if x and x:is_text()
+         and x:get_text() == "−" 
+    then
+      el:add_child_node(x:copy_node(), 1)
+      x:remove_node()
+    end
     local n = el:get_sibling_node(1)
     -- test if next  element is <mo class="MathClass-punc">.</mo>
     if n and n:is_element() 
@@ -357,20 +394,47 @@
   end
 end
 
-local function is_last_element(el)
-  local siblings = el:get_siblings()
-  -- return true only if the current element is the last in the parent's children
-  for i = #siblings, 1, -1 do
-    local curr = siblings[i]
-    if curr == el then
-      return true
-    elseif curr:is_element() then
-      return false
-    end
-  end
-  return false
+local function get_third_parent(el)
+  local first = el:get_parent()
+  if not first then return nil end
+  local second = first:get_parent()
+  if not second then return nil end
+  return second:get_parent()
 end
 
+local function add_space(el, pos)
+  local parent = el:get_parent()
+  local name, prefix = get_element_name(el)
+  local space = create_element(parent, "mspace", prefix)
+  space:set_attribute("width", "0.3em")
+  parent:add_child_node(space, pos)
+end
+
+local function fix_dcases(el)
+	-- we need to fix spacing in dcases* environments
+	-- when you use something like:
+	-- \begin{dcases*}
+	-- 1 & if $a=b$ then
+	-- \end{dcases*}
+	-- the spaces around $a=b$ will be missing
+	-- we detect if the <mtext> elements contains spaces that are collapsed by the browser, and add explicit <mspace>
+	-- elements when necessary
+	if el:get_element_name() == "mtext" then
+		local parent = get_third_parent(el)
+		if parent and parent:get_element_name() == "mtable" and parent:get_attribute("class") == "dcases-star" then
+			local text = el:get_text()
+			local pos = el:find_element_pos()
+			if pos == 1 and text:match("%s$") then 
+				add_space(el, 2)
+			elseif text:match("^%s") and not el._used then
+				add_space(el, pos)
+				-- this is necessary to avoid infinite loop, we mark this element as processed
+				el._used = true
+			end
+		end
+	end
+end
+
 local function is_empty_row(el)
   -- empty row should contain only one <mtd>
   local count = 0
@@ -378,6 +442,9 @@
     for _, child in ipairs(el:get_children()) do
       if child:is_element() then count = count + 1 end
     end
+  else
+    -- row is not empty if it contains any text
+    return false
   end
   -- if there is one or zero childrens, then it is empty row
   return count < 2
@@ -398,6 +465,29 @@
 
 end
 
+local function fix_rel_mo(el)
+  -- this is necessary for LibreOffice. It has a problem with relative <mo> that are
+  -- first childs in an element list. This often happens in equations, where first
+  -- element in a table column is an operator, like non-equal-, less-than etc.
+  local el_name, prefix = get_element_name(el)
+  if el_name == "mo" 
+     and not get_attribute(el, "fence") -- ignore fences
+     and not get_attribute(el, "form")  -- these should be also ignored
+     and not get_attribute(el, "accent") -- and accents too
+  then
+    local parent = el:get_parent()
+    if is_first_element(el) then
+      local mrow = create_element(parent, "mrow", prefix)
+      parent:add_child_node(mrow, 1)
+    elseif is_last_element(el) then
+      local mrow = create_element(parent, "mrow", prefix)
+      parent:add_child_node(mrow)
+    end
+  end
+
+end
+
+
 return function(dom)
   dom:traverse_elements(function(el)
     if settings.output_format ~= "odt" then
@@ -405,6 +495,7 @@
       fix_mfenced(el)
     else
       fix_mo_to_mfenced(el)
+      fix_rel_mo(el)
     end
     fix_radicals(el)
     fix_token_elements(el)
@@ -413,6 +504,7 @@
     fix_numbers(el)
     fix_operators(el)
     fix_mathvariant(el)
+    fix_dcases(el)
     top_mrow(el)
     delete_last_empty_mtr(el)
   end)

Modified: trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-sectionid.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-sectionid.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-sectionid.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -58,7 +58,9 @@
   --- convert table with normalized characters to string
   local name = table.concat(result)
   -- remove spaces
-  return name:gsub("%s+", "-")
+  name = name:gsub("%s+", "-")
+  name = name:gsub("^%-", "")
+  return name
 end
 
 local function parse_toc_line(line)
@@ -78,6 +80,9 @@
   if not mkutils.file_exists(filename) then return nil, "Cannot open TOC file "  .. filename end
   for line in io.lines(filename) do
     local id, name = parse_toc_line(line)
+    -- if section name doesn't contain any text, it would lead to id which contains only number
+    -- this is invalid in HTML
+    if name == "" then name = "_" end
     local orig_name = name
     -- not all lines in the .4tc file contains TOC entries
     if id then
@@ -109,10 +114,20 @@
 end
 
     
+-- we want to remove <a id="xxx"> elements from some elements, most notably <figure>
+local elements_to_remove = {
+  figure = true,
+  figcaption
+}
 
+local function remove_a(el, parent, id)
+  parent:set_attribute("id", id)
+  el:remove_node()
+end
+
 return  function(dom, par)
     local msg
-    toc, msg = toc or parse_toc(par.input .. ".4tc")
+    toc, msg = toc or parse_toc(mkutils.file_in_builddir(par.input .. ".4tc", par))
     msg = msg or "Cannot load TOC"
     -- don't do anyting if toc cannot be found
     if not toc then 
@@ -135,8 +150,16 @@
       local id, href = el:get_attribute("id"), el:get_attribute("href") 
       if id then
         local name = toc[id]
+        local parent = el:get_parent()
+        -- remove unnecessary <a> elements if the parent doesn't have id yet
+        if elements_to_remove[parent:get_element_name()] 
+          and not parent:get_attribute("id") 
+          and el:get_element_name() == "a"
+        then
+          remove_a(el, parent, id)
+          set_id(el, name)
         -- replace id with new section id
-        if name and not toc_ids[name] then
+        elseif name and not toc_ids[name] then
           set_id(el, name)
         else
           if name then

Modified: trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-tablerows.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-tablerows.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/domfilters/make4ht-tablerows.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -4,11 +4,17 @@
     -- detect if the element contains child elements
     local child_elements = 0
     local children = child:get_children()
-    for _, el in ipairs(children) do
+    local last_child_pos
+    for pos, el in ipairs(children) do
+      last_child_pos = pos
       local step = el:is_element() and 1 or 0
       -- log:info("element name", el._name)
       child_elements = child_elements + step
     end
+    -- longtable has <td><p></p></td> inside empty rows, we regard them as empty
+    if child_elements == 1 and children[last_child_pos]:get_element_name() == "p" and child:get_text():gsub("%s", "") == "" then
+      child_elements = 0
+    end
     return child_elements > 0
   end
   local is_empty_row = function(row)

Added: trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-copy_images.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-copy_images.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-copy_images.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -0,0 +1,68 @@
+local M = {}
+local mkutils = require "mkutils"
+local domfilter = require "make4ht-domfilter"
+
+local copied_images = {}
+
+local function image_copy(path, parameters, img_dir)
+  if mkutils.is_url(path) then return nil, "External image" end
+  -- get image basename
+  local basename = path:match("([^/]+)$")
+  -- if outdir is empty, keep it empty, otherwise add / separator
+  local outdir = parameters.outdir == "" and "" or parameters.outdir .. "/"
+  if img_dir ~= "" then 
+    outdir = outdir .. img_dir .. "/"
+  end
+  -- handle trailing //
+  outdir = outdir:gsub("%/+","/")
+  local output_file = outdir .. basename
+  if outdir == "" then
+    mkutils.cp(path, output_file)
+  else
+    mkutils.copy(path, output_file)
+  end
+end
+
+-- filters support only html formats
+function M.test(format)
+  current_format = format
+  if format == "odt" then return false end
+  return true
+end
+
+function M.modify_build(make)
+  local ext_settings = get_filter_settings "copy_images" or {}
+  local img_dir = ext_settings.img_dir or ""
+  local img_extensions = ext_settings.extensions or {"jpg", "png", "jpeg", "svg"}
+  local process = domfilter({
+    function(dom, par)
+      for _, img in ipairs(dom:query_selector("img")) do
+        local src = img:get_attribute("src")
+        if src and not mkutils.is_url(src) then
+          -- remove path specification
+          src = src:match("([^/]+)$")
+          if img_dir ~= "" then
+            src = img_dir .. "/" ..  src
+            src = src:gsub("%/+", "/")
+          end
+          img:set_attribute("src", src)
+        end
+      end
+      return dom
+    end
+  }, "copy_images")
+
+  -- add matcher for all image extensions
+  for _, ext in ipairs(img_extensions) do
+    make:match(ext .. "$", function(path, parameters)
+      image_copy(path, parameters, img_dir)
+      -- prevent further processing of the image
+      return false
+    end)
+  end
+
+  make:match("html$", process, {img_dir = img_dir})
+  return make
+end
+
+return M


Property changes on: trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-copy_images.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-dvisvgm_hashes.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-dvisvgm_hashes.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-dvisvgm_hashes.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -177,7 +177,7 @@
   f:close()
   local dvi_pages = dvireader.get_pages(content)
   -- we must find page numbers and output name sfor the generated images
-  local lg = mkutils.parse_lg(arg.input ..".lg")
+  local lg = mkutils.parse_lg(arg.input ..".lg", arg.builddir)
   for _, name in ipairs(lg.images) do
     local page = tonumber(name.page)
     local hash = dvi_pages[page]

Modified: trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-latexmk_build.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-latexmk_build.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-latexmk_build.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -16,7 +16,7 @@
   -- tex4ht command overwrites content that was set by LaTeX with it's own stuff
   local tmp_file 
   make:add("save_tmp", function(par)
-    local f = io.open(par.input .. ".tmp", "r")
+    local f = io.open(mkutils.file_in_builddir(par.input .. ".tmp", par), "r")
     if f then
       tmp_file = f:read("*all")
       f:close()
@@ -25,7 +25,7 @@
   end)
   make:add("load_tmp", function(par)
     if tmp_file then
-      local f = io.open(par.input .. ".tmp", "w")
+      local f = io.open(mkutils.file_in_builddir(par.input .. ".tmp", par), "w")
       if f then
         f:write(tmp_file)
       end

Modified: trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-staticsite.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-staticsite.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/extensions/make4ht-ext-staticsite.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -67,7 +67,7 @@
   local insert_executed = false
   table.insert(make.matches, 1, {
     pattern=pattern,
-    params = {},
+    params = make.params or {},
     command = function()
       if not insert_executed  then
         fn()
@@ -96,12 +96,17 @@
 
 local function copy_files(filename, par)
   local function prepare_path(dir, subdir)
-    local path = dir .. "/" .. subdir .. "/" .. filename
+    local f = filename
+    if par.builddir then
+        f = f:gsub("^" .. par.builddir .. "/", "")
+    end
+    local path = dir .. "/" .. subdir .. "/" .. f
     return path:gsub("//", "/")
   end
   -- get extension settings
   local site_settings = get_filter_settings "staticsite"
-  local site_root = site_settings.site_root or "./"
+  local site_root = site_settings.site_root or par.outdir 
+  if site_root == "" then site_root = "./" end
   local map = site_settings.map or {}
   -- default path without subdir, will be used if the file is not matched
   -- by any pattern in the map
@@ -150,8 +155,10 @@
     --   match.params.outdir = outdir
     --   print(match.pattern, match.params.outdir)
     -- end
-    make:match("html?$", process)
-    make:match(".*", copy_files, {slug=slug})
+    local params = make.params
+    params.slug = slug
+    make:match("html?$", process, params)
+    make:match(".*", copy_files, params)
   end)
 
   return make

Modified: trunk/Master/texmf-dist/scripts/make4ht/filters/make4ht-domfilter.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/filters/make4ht-domfilter.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/filters/make4ht-domfilter.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -37,10 +37,13 @@
     end
     local input = filter_lib.load_input_file(filename)
     if not input  then return nil, "Cannot load the input file" end
+    -- in pure XML, we need to ignore void_elements provided by LuaXML, because these can exist only in HTML
+    local no_void_elements = {docbook = {}, jats = {}, odt = {}, tei = {} }
+    local void_elements = no_void_elements[parameters.output_format]
     -- we need to use pcall, because XML error would break the whole build process
     -- domobject will be error object if DOM parsing failed
     local status, domobject = pcall(function()
-      return dom.parse(input)
+      return dom.parse(input, void_elements)
     end)
     if not status then
       log:warning("DOM parsing of " .. filename .. " failed:")

Modified: trunk/Master/texmf-dist/scripts/make4ht/formats/make4ht-jats.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/formats/make4ht-jats.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/formats/make4ht-jats.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -32,8 +32,8 @@
 local function process_moves()
   if article_meta then
     if elements_to_move_to_title["article-title"] 
-      and #article_meta:query_selector("title_group") == 0 then -- don't move anything if user added title_group from a config file
-      local title_group = article_meta:create_element("title_group")
+      and #article_meta:query_selector("title-group") == 0 then -- don't move anything if user added title-group from a config file
+      local title_group = article_meta:create_element("title-group")
       for _, name in ipairs{ "article-title", "subtitle" } do
         local v = elements_to_move_to_title[name] 
         if v then
@@ -41,7 +41,7 @@
           v:remove_node()
         end
       end
-      article_meta:add_child_node(title_group)
+      article_meta:add_child_node(title_group, 1)
     end
     if #elements_to_move_to_contribs > 0 then
       local contrib_group = article_meta:create_element("contrib-group")

Modified: trunk/Master/texmf-dist/scripts/make4ht/formats/make4ht-odt.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/formats/make4ht-odt.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/formats/make4ht-odt.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -169,7 +169,7 @@
 local patched_lg_fonts = {}
 local function fix_lgfile_fonts(ignored_name, params)
   -- this function is called from file match. we must use the name of the .lg file
-  local filename = params.input .. ".lg"
+  local filename = mkutils.file_in_builddir(params.input .. ".lg", params)
   if not lg_fonts_processed then
     local lines = {}
     -- default font_size
@@ -209,7 +209,8 @@
 local move_matches = xtpipeslib.move_matches
 
 local function insert_lgfile_fonts(make)
-  local first_file = make.params.input .. ".4oo"
+  local params = make.params
+  local first_file = mkutils.file_in_builddir(params.input .. ".4oo", params)
   -- find the last file and escape it so it can be used 
   -- in filename match
   make:match(first_file, fix_lgfile_fonts)
@@ -310,7 +311,8 @@
       -- odt packing will be done here
       make:match(lastfile, function(filename, par)
         local groups = prepare_output_files(make.lgfile.files)
-        local basename = groups.odt[1]
+        -- we must remove any path from the basename
+        local basename = groups.odt[1]:match("([^/]+)$")
         local odtname = basename .. ".odt"
         local odt,msg = Odtfile.new(odtname)
         if not odt then

Modified: trunk/Master/texmf-dist/scripts/make4ht/make4ht
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/make4ht	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/make4ht	2024-02-23 22:03:29 UTC (rev 70097)
@@ -29,7 +29,7 @@
 
 -- set version number. the template should be replaced by the
 -- actual version number by the build script
-local version = "v0.3m"
+local version = "v0.4"
 mkparams.version_number = version
 
 local args = mkparams.get_args()
@@ -39,6 +39,11 @@
 log:status("Conversion started")
 log:status("Input file: " .. parameters.tex_file)
 
+
+if parameters.builddir and parameters.builddir ~= "" then
+  mkutils.make_path(parameters.builddir)
+end
+
 local mode = parameters.mode
 local build_file = parameters.build_file 
 
@@ -127,9 +132,10 @@
     log:info("No output directory")
     return true
   end
-	log:info("outdir: "..outdir)
-	local outfilename = outdir .. filename
-	mkutils.copy(filename,outfilename)
+  log:info("outdir: "..outdir)
+  local outfilename = filename:gsub("^" .. (par.builddir or ""), "")
+  outfilename = outdir .. outfilename
+  mkutils.copy(filename,outfilename)
 	return true
 end)
 

Modified: trunk/Master/texmf-dist/scripts/make4ht/make4ht-errorlogparser.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/make4ht-errorlogparser.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/make4ht-errorlogparser.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -81,7 +81,29 @@
   return errors
 end
 
+function m.get_missing_4ht_files(log)
+  local used_files = {}
+  local used_4ht_files = {}
+  local missing_4ht_files = {}
+  local pkg_names = {sty=true, cls=true}
+  for filename, ext in log:gmatch("[^%s]-([^%/^%\\^%.%s]+)%.([%w][%w]+)") do
+    -- break ak
+    if ext == "aux" then break end
+    if pkg_names[ext] then
+      used_files[filename .. "." .. ext] = true
+    elseif ext == "4ht" then
+      used_4ht_files[filename] = true
+    end
+  end
+  for filename, _ in pairs(used_files) do 
+    if not used_4ht_files[mkutils.remove_extension(filename)] then 
+      table.insert(missing_4ht_files, filename)
+    end
+  end
+  return missing_4ht_files
+end
 
+
 function m.parse(log)
   local chunks, newtext = get_chunks(log)
   -- save the unparsed text that contains system messages

Modified: trunk/Master/texmf-dist/scripts/make4ht/make4ht-htlatex.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/make4ht-htlatex.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/make4ht-htlatex.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -5,7 +5,7 @@
 local Make = Make or {}
 -- this function reads the LaTeX log file and tries to detect fatal errors in the compilation
 local function testlogfile(par)
-  local logfile = par.input .. ".log"
+  local logfile = mkutils.file_in_builddir(par.input .. ".log", par)
   local f = io.open(logfile,"r")
   if not f then
     log:warning("Make4ht: cannot open log file "..logfile)
@@ -29,6 +29,9 @@
       end
     end
   end
+  -- info about packages with no corresponding .4ht files
+  local missing_4ht = error_logparser.get_missing_4ht_files(content)
+  for _, filename in ipairs(missing_4ht) do log:info("Unsupported file: " .. filename) end
   -- test for fatal errors
   if text:match("No pages of output") or text:match("TeX capacity exceeded, sorry") or text:match("That makes 100 errors") or text:match("Emergency stop") then return 1 end
   return 0
@@ -40,18 +43,17 @@
 --env.Make:add("htlatex", "${htlatex} ${latex_par} '\\\makeatletter\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\g at addto@macro\\@documentclasshook{\\RequirePackage[#1,html]{tex4ht}\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{\\documentstyle[tex4ht]}}}\\makeatother\\HCode '${config}${tex4ht_sty_par}'.a.b.c.\\input ' ${input}")
 
 -- template for calling LaTeX with tex4ht loaded
-Make.latex_command = "${htlatex} --interaction=${interaction} ${latex_par} '\\makeatletter"..
+Make.latex_command = "${htlatex} --interaction=${interaction} ${build_dir_arg} ${latex_par} '\\makeatletter"..
 "\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode"..
 "\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else"..
-"\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{\\AddToHook"..
-"{class/before}{\\RequirePackage[#1,html]{tex4ht}${packages}}"..
+"\\expandafter\\Link\\fi}\\def\\Link#1.a.b.c.{"..
 "\\let\\HCode\\documentstyle\\def\\documentstyle{\\let\\documentstyle"..
 "\\HCode\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\def"..
 "\\HCode####1{\\documentstyle[tex4ht,}\\@ifnextchar[{\\HCode}{"..
-"\\documentstyle[tex4ht]}}}\\makeatother\\HCode ${tex4ht_sty_par}.a.b.c."..
+"\\documentstyle[tex4ht]}}\\RequirePackage[#1,html]{tex4ht}${packages}}\\makeatother\\HCode ${tex4ht_sty_par}.a.b.c."..
 "\\input \"\\detokenize{${tex_file}}\"'"
 
-Make.plain_command = '${htlatex} --interaction=${interaction} ${latex_par}' ..
+Make.plain_command = '${htlatex} --interaction=${interaction} ${build_dir_arg} ${latex_par}' ..
 "'\\def\\Link#1.a.b.c.{\\expandafter\\def\\csname tex4ht\\endcsname{\\expandafter\\def\\csname tex4ht\\endcsname{#1,html}\\input tex4ht.sty }}" ..
 "\\def\\HCode{\\futurelet\\HCode\\HChar}\\def\\HChar{\\ifx\"\\HCode\\def\\HCode\"##1\"{\\Link##1}\\expandafter\\HCode\\else\\expandafter\\Link\\fi}" ..
 "\\HCode ${tex4ht_sty_par}.a.b.c.\\input \"\\detokenize{${tex_file}}\"'"
@@ -68,7 +70,11 @@
     devnull = " > nul 2>&1"
   end
   par.interaction = par.interaction or "batchmode"
-  -- remove all terminal output from the batchmode 
+  if par.builddir~="" then
+      par.build_dir_arg = "--output-directory=${builddir}" % par
+  else
+      par.build_dir_arg = ""
+  end
   if par.interaction == "batchmode" then
     command = command .. devnull
   end

Modified: trunk/Master/texmf-dist/scripts/make4ht/make4ht-indexing.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/make4ht-indexing.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/make4ht-indexing.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -80,7 +80,7 @@
     elseif line:match("^\\indexentry") then
       -- replace the page number with the current
       -- index entry number
-      local result = line:gsub("{[0-9]+}$", "{"..current_entry .."}")
+      local result = line:gsub("%b{}$", "{"..current_entry .."}")
       buffer[#buffer+1] = get_utf8(result)
     else
       buffer[#buffer+1] = line
@@ -150,7 +150,7 @@
 end
 
 local prepare_tmp_idx = function(par)
-  par.idxfile = get_idxname(par)
+  par.idxfile = mkutils.file_in_builddir(get_idxname(par), par)
   if not par.idxfile or not mkutils.file_exists(par.idxfile) then return nil, "Cannot load idx file " .. (par.idxfile or "''") end
   -- construct the .ind name, based on the .idx name
   par.indfile = par.indfile or par.idxfile:gsub("idx$", "ind")

Modified: trunk/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/make4ht-lib.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -194,9 +194,12 @@
 			end
 		end
 	end
-	local lgfile = params.input and params.input .. ".lg" or nil 
+  local lgfile = params.input and params.input .. ".lg" or nil
+  if params.builddir~="" then 
+    lgfile = params.builddir .. "/" .. lgfile
+  end
 	if lgfile then
-   	self.lgfile = self.lgfile or mkutils.parse_lg(lgfile)
+   	self.lgfile = self.lgfile or mkutils.parse_lg(lgfile, params.builddir)
     local lg = self.lgfile
 		-- First convert images from lg files
 		self:image_convert(lg["images"])

Modified: trunk/Master/texmf-dist/scripts/make4ht/mkparams.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/mkparams.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/mkparams.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -21,6 +21,7 @@
                 possible values: tex4ht or lua4ht
   -c,--config (default xhtml) Custom config file
   -d,--output-dir (default nil)  Output directory
+  -B,--build-dir (default nil)  Build directory
   -e,--build-file (default nil)  If build file is different than `filename`.mk4
   -f,--format  (default html5)  Output file format
   -h,--help  Display this message
@@ -191,12 +192,20 @@
 	local outdir = ""
 	local packages = ""
 
-	if  args["output-dir"] ~= "nil" then 
+	if  args["output-dir"] ~= "nil" then
 		outdir =  args["output-dir"]  or ""
 		outdir = outdir:gsub('\\','/')
 		outdir = outdir:gsub('/$','')
 	end
 
+	local builddir = ""
+
+	if  args["build-dir"] ~= "nil" then
+		builddir =  args["build-dir"]  or ""
+		builddir = builddir:gsub('\\','/')
+		builddir = builddir:gsub('/$','')
+	end
+
   -- make4ht now requires UTF-8 output, because of DOM filters
   -- numeric entites are expanded to Unicode characters. These
   -- characters would be displayed incorrectly in 8 bit encodings.
@@ -274,7 +283,7 @@
 	local parameters = {
 		htlatex = compiler
 		,input=input
-    ,tex_file=tex_file
+        ,tex_file=tex_file
 		,packages=packages
 		,latex_par=table.concat(latex_params," ")
 		--,config=ebookutils.remove_extension(args.config)
@@ -291,6 +300,7 @@
 		--,t4ht_dir_format=t4ht_dir_format
 	}
 	if outdir then parameters.outdir = outdir end
+	if builddir then parameters.builddir = builddir end
 	log:info("Output dir: "..outdir)
 	log:info("Compiler: "..compiler)
 	log:info("Latex options: ".. table.concat(latex_params," "))

Modified: trunk/Master/texmf-dist/scripts/make4ht/mkutils.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/make4ht/mkutils.lua	2024-02-23 22:03:10 UTC (rev 70096)
+++ trunk/Master/texmf-dist/scripts/make4ht/mkutils.lua	2024-02-23 22:03:29 UTC (rev 70097)
@@ -25,7 +25,11 @@
 --print( "${name} is ${value}" % {name = "foo", value = "bar"} )
 -- Outputs "foo is bar"
 
+function is_url(path)
+  return path:match("^%a+://")
+end
 
+
 -- merge two tables recursively
 function merge(t1, t2)
   for k, v in pairs(t2) do
@@ -81,8 +85,9 @@
 
 
 -- searching for converted images
-function parse_lg(filename)
+function parse_lg(filename, builddir)
   log:info("Parse LG")
+  local dir = builddir~="" and builddir .. "/" or ""
   local outputimages,outputfiles,status={},{},nil
   local fonts, used_fonts = {},{}
   if not file_exists(filename) then
@@ -95,14 +100,15 @@
       line:gsub("needs %-%-%- (.+)%[([0-9]+)%] ==> (.*) %-%-%-",
       function(file,page,output) 
         local rec = {
-          source = file,
+          source=file,
           page=page,
-          output = output
+          output=dir..output
         }
         table.insert(outputimages,rec)
       end
       )
-      line:gsub("File: (.*)",  function(k) 
+      line:gsub("File: (.*)",  function(k)
+        k = dir .. k
         if not usedfiles[k] then
           table.insert(outputfiles,k)
           usedfiles[k] = true
@@ -133,6 +139,10 @@
 -- in reality it isn't.
 -- local cp_func = os.type == "unix" and "mv" or "move"
 function cp(src,dest)
+  if is_url(src) then
+    log.info(src .. " is a URL, will leave as is")
+    return
+  end
   if not file_exists(src) then
     -- try to find file using kpse library if it cannot be found
     src = kpse.find_file(src) or src
@@ -212,14 +222,33 @@
   if type(dirs) ~="table" then
     return false, "mkdirectories: dirs is not table"
   end
+  local path = ""
   for _,d in ipairs(dirs) do
-    local stat,msg = lfs.mkdir(d)
+    path = path .. d .. "/"
+    local stat,msg = lfs.mkdir(path)
     if not stat then return false, "makedirectories error: "..msg end
-    lfs.chdir(d)
   end
   return true
 end
 
+function make_path(path)
+  -- we must create the build dir if it doesn't exist
+  local cwd = lfs.currentdir()
+  -- add dummy /foo dir. it won't be created, but without that, the top-level dir wouldn't be created
+  local parts = mkutils.prepare_path(path .. "/foo")
+  local to_create = mkutils.find_directories(parts)
+  mkutils.mkdirectories(to_create)
+  -- change back to the original dir
+  lfs.chdir(cwd)
+end
+
+function file_in_builddir(filename, par)
+  if par.builddir and par.builddir ~= "" then
+    return par.builddir .. "/" .. filename
+  end
+  return filename
+end
+
 function copy_filter(src,dest, filter)
   local src_f=io.open(src,"rb")
   local dst_f=io.open(dest,"w")
@@ -379,7 +408,7 @@
   par.expanded = command % par
   -- quotes in latex_command must be escaped, they cause Latexmk error
   par.expanded = par.expanded:gsub('"', '\\"')
-  local newcommand = 'latexmk  -pdf- -ps- -auxdir=. -outdir=. -latex="${expanded}" -dvi -jobname=${input} ${tex_file}' % par
+  local newcommand = 'latexmk  -pdf- -ps- -auxdir=${builddir} -outdir=${builddir} -latex="${expanded}" -dvi -jobname=${input} ${tex_file}' % par
   log:info("LaTeX call: " .. newcommand)
   os.execute(newcommand)
   return Make.testlogfile(par)
@@ -392,7 +421,7 @@
   -- detect if svg output is used
   -- if yes, we need to pass the -g.svg option to tex4ht command
   -- to support svg images for character pictures
-  local logfile = par.input .. ".log"
+  local logfile = mkutils.file_in_builddir(par.input .. ".log", par)
   if file_exists(logfile) then
     for line in io.lines(logfile) do
       local options = line:match("TeX4ht package options:(.+)")
@@ -405,18 +434,35 @@
       end
     end
   end
+  local cwd = lfs.currentdir()
+  if par.builddir~="" then
+      lfs.chdir(par.builddir)
+  end
   local command = "tex4ht ${tex4ht_par} \"${input}.${dvi}\"" % par
   log:info("executing: " .. command)
-  return execute(command)
+  local status, output = execute(command)
+  lfs.chdir(cwd)
+  return status, output
 end
 , nil, 1)
-env.Make:add("t4ht","t4ht ${t4ht_par} \"${input}.${ext}\"",{ext="dvi"},1)
+env.Make:add("t4ht", function(par)
+    par.ext = "dvi"
+    local cwd = lfs.currentdir()
+    if par.builddir ~= "" then
+        lfs.chdir(par.builddir)
+    end
+    local command = "t4ht ${t4ht_par} \"${input}.${ext}\"" % par
+    log:info("executing: " .. command)
+    execute(command)
+    lfs.chdir(cwd)
+end
+)
 
 env.Make:add("clean", function(par)
   -- remove all functions that process produced files
   -- we will provide only one function, that remove all of them
   Make.matches = {}
-  local main_name = par.input
+  local main_name = mkutils.file_in_builddir( par.input, par)
   local remove_file = function(filename)
     if file_exists(filename) then
       log:info("removing file: " .. filename)
@@ -424,7 +470,8 @@
     end
   end
   -- try to find if the last converted file was in the ODT format
-  local lg_file = parse_lg(main_name .. ".lg") 
+  local lg_name =  main_name .. ".lg"
+  local lg_file = parse_lg(lg_name, par.builddir)
   local is_odt = false
   if lg_file and lg_file.files then
     for _, x in ipairs(lg_file.files) do
@@ -442,7 +489,7 @@
   end
   Make:match("tmp$", function()
     -- remove temporary and auxilary files
-    for _,ext in ipairs {"aux", "xref", "tmp", "4tc", "4ct", "idv", "lg","dvi", "log", "ncx"} do
+    for _,ext in ipairs {"aux", "xref", "tmp", "4tc", "4ct", "idv", "lg","dvi", "log", "ncx", "idx", "ind"} do
       remove_file(main_name .. "." .. ext)
     end
   end)
@@ -592,7 +639,13 @@
   local is_extension_file = find_lua_file(extension_library)
   -- don't try to load the extension if it doesn't exist
   if not is_extension_file then return nil, "cannot fint extension " .. name  end
-  local extension = require("make4ht.extensions.make4ht-ext-".. name)
+  local extension = nil
+  local local_extension_path = package.searchpath(extension_library, package.path)
+  if local_extension_path then
+      extension = dofile(local_extension_path)
+  else
+      extension = require("make4ht.extensions.make4ht-ext-".. name)
+  end
   -- extensions can test if the current output format is supported
   local test = extension.test
   if test then



More information about the tex-live-commits mailing list.