将 Latex \label 转换为 HTML ID 属性

将 Latex \label 转换为 HTML ID 属性

我们的组织已将参考手册以 LaTeX 格式发布。这是一本基本的 300 页参考手册,包含表格、图表和章节。该组织还拥有出版物数据库,用于跟踪文档的提交和流程。数据库中有帮助“窗口”。他们希望将帮助链接到 Latex 参考手册的特定部分,以便当用户单击帮助按钮时,手册的特定部分就会显示出来。我查看过 PDF 目标链接,但我认为这太费力,而且并不总是显示正确的位置。实用htlatex程序似乎是可行的方法。问题是:

\label标记 LaTeX 文件以便转换为 HTML属性的最佳方法是什么id

下面是我的 LaTeX 文件的示例:

\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}.

以下是我的 HTML 输出:

<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 heading
<a href="#x1-2r1">1<!--tex4ht:ref: sec:intro --></a>.

我希望 id<a><a id="sec:intro">

答案1

您可以尝试以下配置文件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

您可以使用以下命令来使用它:

htlatex filename.tex "config"

或者,最好htlatex使用

make4ht -c config.cfg filename.tex

您可以阅读有关两者的区别这里

在配置文件中,我们重新定义\label插入额外的<a id="[label]"></a>标签。它使用\label参数来构造你可以链接到的id。要使用此idfrom\ref命令,我们需要提供一个\Configure{ref}命令。详细描述在配置文件中的注释中。

现在有一个问题。由于\label使用 after \section,目标链接插入在 section 之后,这将导致用户在单击标签的超链接时将结束 section 下方的页面。查看生成的 HTML 代码:

 <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>  

这个问题在 TeX 级别很难解决,所以我建议另一种选择,即使用make4ht构建文件。

make4ht支持 Lua 构建文件。它们可用于对生成的 HTML 文件进行后处理。它甚至支持 LuaXML DOM 对象,因此我们可以轻松遍历文档并更新所有<a>元素。

我们可以使用.aux文件中存储的信息。标签与 TeX4ht 分配的之间的映射id以以下形式存储在这里:

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

可以看到标签存放在 的第一个参数中\newlabel,ID 是 的第一个参数\rEfLiNK

这是构建文件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)

此构建文件首先解析 AUX 文件并生成 TeX4ht 指定的 ID 属性与原始标签之间的映射。然后它循环遍历所有<a>元素并更新 ID 和超链接。

您可以使用以下命令编译您的文档:

make4ht -e build.lua filename.tex

结果如下:

<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> 

结束语

我看得出您可能使用了自定义类,因为您的 HTML 代码片段生成的是<p>元素而不是<h3>部分。您可能希望为您的类创建一个配置文件,以获得结构更好的 HTML 文件。

当您为章节生成正确的标签时,当前版本的制作4小时(的现代替代品htlatex)产生以下代码:

<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> 

因此,它会根据章节标题生成稳定的超链接目标。“章节标题”变为<h3 id="section-title">。对于您的用例来说,这可能比使用\label名称更好。

支持上述功能的版本make4ht尚未在 CTAN 上,因此您可能确实想使用\label我建议的方法之一来使用目前生成的所有超链接的标签。

答案2

你给我设定了相当大的挑战!我也觉得 Michal 的方法很有趣,但这个方法不需要 Lua 的额外步骤,只需要htlatex你一直在使用的。

在你的序言中,添加:

\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

并在文档正文中

\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

此设置目前仅适用于\section-commands。不幸的是,我现在没有时间研究如何全面一致地执行此操作。此外,此方法依赖于\label紧随其后的\section(这是您的 MWE 中的情况),因为在放置时\section,我们需要知道id要附加在那里的。如果不是\label紧随其后,那么整个代码就会安全地恢复使用自动生成的 ID。

我使事情正常运行的完整代码:https://pastebin.com/y1rD5yPE

相关内容