5

This is related to my previous question. The use-case is still the same: I want to convert a document into HTML, and give the links in the generated HTML a permanent character, even when the structure of my document changes. This to allow to link from an external source to the generated HTML, and keep these links valid even when I update the generated HTML.

When you convert a latex document to PDF, you can achieve this by using the destlabel=true option of the hyperref package. For example, in the following code

\documentclass{book}
\usepackage[destlabel=true]{hyperref}
\begin{document}
\tableofcontents
\listoffigures
\listoftables
\section{First section}\label{first_section}
Reference to the \autoref{first_section}

\begin{figure}[h]
  \caption{My invisible figure}
  \label{invisible_figure}
\end{figure}

Reference to the \autoref{invisible_figure}

\begin{table}
  \caption{My invisible table}
  \label{invisible_table}
\end{table}

Reference to the \autoref{invisible_table}
\end{document}

the links in the text, table of contents, list of figures, ... all use the label specified in the input file. For example the link in the list of figures points to test.pdf#invisible_figure.

However, when you run this through htlatex test.tex "xhtml,3", that same link becomes testse1.html#x5-40011.

Is there a way to replace the x5-40011 part with the contents of the label from the input file ? Or with something else I can control from my input file to give it a permanent character ?

7

It is possible to postprocess the html files using make4ht filters. We can find corresponding id attributes for labels in the aux file:

\newlabel{invisible_figure}{{\rEfLiNK{x5-40011}{\csname :autoref\endcsname{figure}1}}{\rEfLiNK{x5-40011}{\csname :autoref\endcsname{figure}9}}{\rEfLiNK{x5-40011}{\csname :autoref\endcsname{figure}My invisible figure}}{figure.1}{}}

with make4ht, we can use following function to parse the aux file for labels:

local labels = {} 

Make:add("parseaux", function(params)
  local auxname = params.input .. ".aux"
  for line in io.lines(auxname) do
    line:gsub("newlabel%{([^%}]+)%}%{%{.rEfLiNK%{([^%}]+)", function(label,link)
      -- insert links to table so we can search for labels
      labels[link] = label
    end )
  end
end)

Make:parseaux {} 

gsub function has useful feature that it can use function to process regex matches, so we will use it extensively in our example. In this snippet, we just saved labels to the table together with links generated for them by tex4ht.

we then need to process all html files and replace links saved in labels table in id and href attributes:

local function replace_labels(html)
  -- match element start
  html = html:gsub("<([a-z0-9A-Z]+)(%s+[^%>]-)>", function(element, attr)
    -- process attributes
    local attr = attr:gsub('([a-z]+)%s*=%s*"([^%"]-)"', function(k,v)
       -- process only id or href attributes
       if k=="id" or k == "href" then
         -- support for links with filenames, we are interested only in the part after '#'
         v = v:gsub("([^%#]+)$", function(link)
           local x = labels[link] or link
           return x
         end)
       end
      return string.format('%s="%s"',k,v)
    end)
    return string.format('<%s %s>', element,  attr)
  end)
  return html
end
local process = filter{replace_labels}

Make:match("html$", process)

and this is complete sample.mk4 file:

local filter = require "make4ht-filter"

-- add command for parsing the aux file for labels and corresponding id
-- aux file contains records similar to:
-- \newlabel{first_section}{{\rEfLiNK{x1-40000.1}
local labels = {} 

Make:add("parseaux", function(params)
  local auxname = params.input .. ".aux"
  for line in io.lines(auxname) do
    line:gsub("newlabel%{([^%}]+)%}%{%{.rEfLiNK%{([^%}]+)", function(label,link)
      -- insert links to table so we can search for labels
      labels[link] = label
    end )
  end
end)

if mode=="draft" then
  Make:htlatex()
else
  Make:htlatex()
  Make:htlatex()
  Make:htlatex()
end


Make:parseaux {}

local function replace_labels(html)
  -- match element start
  html = html:gsub("<([a-z0-9A-Z]+)(%s+[^%>]-)>", function(element, attr)
    -- process attributes
    local attr = attr:gsub('([a-z]+)%s*=%s*"([^%"]-)"', function(k,v)
       -- process only id or href attributes
       if k=="id" or k == "href" then
         -- support for links with filenames, we are interested only in the part after '#'
         v = v:gsub("([^%#]+)$", function(link)
           local x = labels[link] or link
           return x
         end)
       end
      return string.format('%s="%s"',k,v)
    end)
    return string.format('<%s %s>', element,  attr)
  end)
  return html
end
local process = filter{replace_labels}

Make:match("html$", process)

compile your file with:

make4ht -e sample.mk4 filename "3"

and this is the result:

 <h3  class="sectionHead"><span  class="titlemark">0.1   </span> <a  
 id="first_section"></a>First section</h3>
<!--l. 8--><p  class="noindent" >Reference to the <a  
href="#first_section">section&#x00A0;0.1</a>
</p>
   <hr  class="figure" /><div  class="figure" 
>
<a  
 id="invisible_figure"></a>
<br  /> <div  class="caption" 
><span  class="id">Figure&#x00A0;1: </span><span   
class="content">My invisible figure</span></div><!--tex4ht:label?: x5-40011 -->
   </div><hr  class="endfigure" />
<!--l. 15--><p  class="indent" >   Reference to the <a  
href="#invisible_figure">Figure&#x00A0;1</a>
</p>
   <div  class="table">
<!--l. 18--><p  class="indent" >   <a  
 id="invisible_table"></a></p><hr  class="float" /><div  class="float" 
>
 <div  class="caption" 
><span  class="id">Table&#x00A0;1: </span><span   
class="content">My invisible table</span></div><!--tex4ht:label?: x5-40021 -->
   </div><hr  class="endfloat" />
   </div>
<!--l. 22--><p  class="indent" >   Reference to the <a  
href="#invisible_table">Table&#x00A0;1</a> </p>

it works also for inter-file links:

 <div  class="tableofcontents"><span  class="lotToc" >1&#x00A0;<a  
href="samplese1.html#invisible_table">My invisible table</a></span><br  />
   </div>
   <div  class="sectionTOCS">
   &#x00A0;<span  class="sectionToc" >0.1 <a  
href="samplese1.html#first_section">First section</a></span>
   </div>
  • Thanks. This works brilliant, and the answer is written in such a way that somebody without much tex knowledge can still understand it. I only had some trouble installing make4ht, but I logged an issue for this on GitHub. – Robin Aug 12 '15 at 15:01
  • @Robin Lua is more easy to understand than TeX, fortunately :) – michal.h21 Aug 12 '15 at 17:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.