1

Our organization has their reference manual published in LaTeX. It is a basic 300 page reference manual with tables, figures and sections. The organization also has publications database to track the submission and process flow of documents. In the database, their are help windows. They want to link the help to specific parts of the Latex reference manual so when the user clicks on the help button, the specific part of the manual displays. I have looked at PDF destination links but I think that is too labour intensive and will not always display the proper location. The htlatex utility seems to be the way to go. Here's the question:

What is the best way to markup the LaTeX file so the \label converts to the HTML ID attribute?

Below is the example of my LaTeX file:

\section{Section heading}\label{sec:intro}

Blah blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah.  Here is a citation\,\cite{EXAMPLE}.

Blah blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah.

This is a test to link to Section this text is for searching in HTML Karen heading \ref{sec:intro}.

Below is my HTML output:


        `<a id="x1-2r1"></a>
             <!--l. 80--><p class="noindent" ><span class="phvb8t-x-x-164">1</span>
        <span class="phvb8t-x-x-164">Section</span>
             <span class="phvb8t-x-x-164">heading</span>
        <a id="Q1-1-0"></a><!--l. 80--><p class="noindent" 
    
    >__________________________________________________________________________________
    <!--l. 82--><p class="noindent" >Blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah. Here is a
    citation <span class="cite">[<a href="#XEXAMPLE">1</a>]</span>.
    <!--l. 86--><p class="noindent" >Blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah.
    <!--l. 90--><p class="noindent" >This is a test to link to Section this text is for searching in HTML Karen heading
    <a href="#x1-2r1">1<!--tex4ht:ref: sec:intro --></a>.`

I would like the id on `<a to <a id="sec:intro">`

Thank you for any recommendations and help.
8
  • Welcome to TeX.SE... Can you post your MWE in a workable format, i.e., from \documentclass to \end{document}... – MadyYuvi May 19 at 13:37
  • You're using pandoc for the conversion? – Vincent Kuhlmann May 19 at 15:01
  • Ah nevermind, you stated you used htlatex – Vincent Kuhlmann May 19 at 15:08
  • HTML id attribute cannot contain colon, you would get validation error. – michal.h21 May 19 at 15:48
  • 1
    @VincentKuhlmann sorry, I was wrong. it is not actually prohibited, but it will cause problems when you want to use such ID in CSS file, because colons have special meaning here. that probably confused me. – michal.h21 May 19 at 16:45
0

You can try the following configuration file, config.cfg:

\Preamble{xhtml}
\catcode`\:=11
% redefine \label to output link with the current label as id
% \o:label: calls the original label
\def\:tempa#1{\Link{}{#1}\EndLink\o:label:{#1}}
\HLet\label\:tempa
% configure \ref
% \Link{\RefArg}{} starts link. \RefArg contains the label
% we use \:gobble to remove the id generated by TeX4ht, it would 
% end in the hyperlinked text otherwise
\Configure{ref}{\Link{\RefArg}{}\:gobble}{\EndLink}{}
\catcode`\:=12
\begin{document}
\EndPreamble

You can use it with the following command:

htlatex filename.tex "config"

or, preferably, as htlatex is deprecated with

make4ht -c config.cfg filename.tex

You can read about the differences about the two here

In the configuration file, we redefine \label to insert extra <a id="[label]"></a> tag. It uses the \label argument to construct the id that you can link to. To use this id from \ref command, we need to provide a \Configure{ref} command. Detailed description is in the comments in the configuration file.

Now, there is one issue. As \label is used after \section, the destination link is inserted after section, which will result that user will end below section when he clicks on the hyperlink to the label. See the generated HTML code:

 <h3 class='sectionHead' id='section-heading'><span class='titlemark'>1   </span> <a id='x1-10001'></a>Section heading</h3>
<!-- l. 4 --><p class='noindent'><a id='sec:intro'></a>
...
</p><!-- l. 14 --><p class='indent'>   This is a test to link to Section this text is for searching in HTML Karen heading
<a href='#sec:intro'>1</a>. </p>  

This issue is difficult to solve on TeX level, so I would suggest another option, which is to use make4ht build files.

make4ht supports Lua build files. They can be used to post-process the generated HTML files. It even supports LuaXML DOM object, so we can easily traverse the document and update all <a> elements.

We can use information stored in the .aux file. The mapping between label and the id assigned by TeX4ht is stored here in the following form:

\newlabel{sec:intro}{{\rEfLiNK{x1-10001}{1}}{\rEfLiNK{x1-10001}{1}}}

You can see that the label is stored in the first parameter to \newlabel, ID is the first parameter to \rEfLiNK.

Here is the build file, build.lua:

local domfilter = require "make4ht-domfilter"

-- we need to parse the aux file to get mapping between labels and id attribute
-- of the corresponding <a> element
local function parse_aux(aux_filename)
  local mapping = {}
  local f = io.open(aux_filename, "r")
  local content = f:read("*all")
  f:close()
  -- labels are stored in this form:
  -- \newlabel{sec:intro}{{\rEfLiNK{x1-10001}{1}}{\rEfLiNK{x1-10001}{1}}}
  for label, id in content:gmatch("newlabel{(.-)}{{.-{(.-)}") do
    mapping[id] = label
  end
  return mapping
end

-- this will hold table with mapping between existing ids and labels
local aux

-- chain of functions that will process all generated HTML files
local process = domfilter {
  function(dom, properties)
    -- because there can be multiple HTML file, we want to reuse existing 
    -- mapping when we process subsequent files
    aux = aux or parse_aux(properties.input .. ".aux")
    -- process all <a> elements and replace ids generated by TeX4ht by original labels
    for _, a in ipairs(dom:query_selector("a")) do
      -- update id attributes
      local id = a:get_attribute("id")
      if aux[id] then a:set_attribute("id", aux[id]) end
      local href = a:get_attribute("href")
      -- fix links
      if href then
        -- get url and id from the hyperlink
        local url, id = href:match("(.*)#(.+)")
        if id and aux[id] then
          -- update hyperlink
          a:set_attribute("href", string.format("%s#%s", url or "", aux[id]))
        end
      end
    end
    return dom
  end
}

Make:match("html$", process)

This build file first parses the AUX file and produces mapping between ID attributes assigned by TeX4ht and the original label. It then loops over all <a> elements and updates IDs and hyperlinks.

You can compile your document using this command:

make4ht -e build.lua filename.tex

This is the result:

<h3 class='sectionHead'><span class='titlemark'>1   </span> <a id='sec:intro'></a>Section heading</h3>
...
<p class='indent'>   This is a test to link to Section this text is for searching in HTML Karen heading
<a href='#sec:intro'>1<!-- tex4ht:ref: sec:intro  --></a>. </p> 

Closing remarks

I can see that you probably use a custom class, as your HTML snippet produces <p> element instead of <h3> for section. You will probably want to create a configuration file for your class, to get a better structured HTML file.

When you produce correct tags for sections, the current version of make4ht (modern replacement for htlatex) produces the following code:

<h3 id='section-heading' class='sectionHead'><span class='titlemark'>1   </span> <a id='x1-10001'></a>Section heading</h3>
...
<p class='indent'>   This is a test to link to Section this text is for searching in HTML Karen heading
<a href='#section-heading'>1<!-- tex4ht:ref: sec:intro  --></a>. </p> 

So it generates stable hyperlink destinations based on section titles. "Section title" becomes <h3 id="section-title">. This may be even better for your use case than to use \label names.

The version of make4ht that supports the above feature is not on CTAN yet, so you may really want to use the labels all hyperlinks produced by \label at the moment using one of methods I suggested.

0

That was quite a challenge you put me up to! I find Michal's approach quite intriguing too, but this one is without the extra step of Lua, and requires only the htlatex you've been using.

In your preamble, add:

\makeatletter
% We are going to modify \section to include a div with the label as id.
\let\orig@section\section
\renewcommand\section[1]{%
    \def\@sectiontitle{#1}%
    \futurelet\@aftersectioncommand\resolvesection
}

% In case you disable the code, don't have the aux throw errors.
\def\writeSetHrefFallback{
    \write\@auxout{\protect\ifx\protect\sethrefaccessible\protect\undefined
        \protect\typeout{HTMLLabels: Fallback was used!}
        \protect\def\protect\sethrefaccessible \string##1{}
        \protect\fi}%
}

% This is triggered when on previous compilation, a div with the label as id of #1 had been placed.
\def\hrefaccessible{}

\def\sethrefaccessible#1{
    \expandafter\global\expandafter\let\csname hrefaccessible@#1\endcsname\hrefaccessible
}

% Check if there is a \label following the \section
\def\resolvesection{%
    \let\@storedlabel\relax
    \ifx\@aftersectioncommand\label
        \typeout{HTMLLabels: Section followed by label}%
        \let\orig@label\label
        % Consume the label
        \def\label##1{%
            \def\@storedlabel{##1}%
            \def\curhypid{##1}%
            \write\@auxout{\protect\sethrefaccessible{##1}}%
            % We have acquired the necessary information, now relay everything back to the normal commands
            \let\label\orig@label
            \expandafter\orig@section\expandafter{\@sectiontitle}%
            \label{##1}%
        }%
    \else
        % Fallback: \label not following \section directly
        \let\curhypid\relax
        \typeout{HTMLLabels: Section not followed by label}%
        %
        \expandafter\orig@section\expandafter{\@sectiontitle}%
    \fi
}

% A variation of ref which can use labels as href to ids
\let\orig@ref\ref
\newcommand\htmllabelref[1]{%
    \expandafter\ifx\csname hrefaccessible@#1\endcsname\hrefaccessible
        % Upon previous compilation, the label had been assigned as an id to a div. We disable the normal link of \ref, and put one in ourselves.
        \begingroup
        \def\rEfLiNK##1##2{##2}%
        \Link{#1}{}%
        \orig@ref{#1}%
        \EndLink
        \endgroup
    \else
        \orig@ref{#1}%
    \fi
}
\makeatother

and in the document body

\makeatletter
\writeSetHrefFallback

\LinkCommand\HeaderTarget{div,href,id}

\def\curhypid{sec:unknown}

\Configure{section}
{}{}  
{%
\HCode{<h3>}
\ifx\curhypid\relax
    \PackageWarning{HTMLLabels}{Section misses label!}
\else
    \HeaderTarget{}{\curhypid}\EndHeaderTarget
\fi
\TitleMark\space}
{\HCode{</h3>}}

\ConfigureMark{section}{\thesection}

%% BEGIN This regions sets all \ref to act as \htmllabelref
\def\ref{\htmllabelref}

\let\orig@T@ref\T@ref
\def\alt@T@ref#1{%
    \let\T@ref\orig@T@ref
    \htmllabelref{#1}%
    \let\T@ref\alt@T@ref
}
\let\T@ref\alt@T@ref
%% END

\makeatother

This setup is currently only for \section-commands. I unfortunately don't have the time right now to investigate how to do it consistently across the board. Also, this method relies on the \label coming right after the \section (which was the case in your MWE), because when placing the \section, we need to know the id to be attached there. If the \label doesn't come right after, then the whole code falls back safely to using the autogenerated id's.

The full code in which I got things working: https://pastebin.com/y1rD5yPE

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.