具有多个索引和使用 tex4ebook 的 Pagenotes - 需要支持

具有多个索引和使用 tex4ebook 的 Pagenotes - 需要支持

请参阅\pagenote 中的 \index 带有 splitindex 选项(用于多个索引)

使用以下 MWE,无法使用 tex4ebook 制作 epub。

\documentclass{report}
\usepackage{index}
\usepackage[texindy,splitindex]{imakeidx}
\usepackage[hyperindex,hidelinks]{hyperref}
\usepackage[page]{pagenote}

\input{pagenote-index-usageB.tex}% See code below
\makeindex[program=texindy,name=Names,title=Names]
\makeindex[program=texindy,name=Something,title=Something]
\makepagenote

\begin{document}
    
    First index.\index[Something]{First index} 
    
    Sentence.\pagenote{First pagenote without index.}
    
    \clearpage
    
    Emph index.\index[Something]{Emph index@\emph{Emph index}} 
    
    pagenote and index.\pagenote{Second pagenote with index.\protect\is[Something]{Index inside second pagenote|seealso{Emph index}}}
    
    \clearpage
    
    Third pagenote.\pagenote{\emph{Emph index word}.\protect\is[Something]{Emph index@\emph{Emph index}}\protect\is[Something]{Emph index@\emph{Emph index}|see{Index inside second pagenote}}}
    
    Namenindex\index[Names]{Namenindex}
    
    \printnotes
    
    \printindex[Something]
    \printindex[Names]
    
\end{document}

借助文件pagenote-index-usageB.tex

\newcommand\pn[2]{\hyperpage{#2}\hyperlink{pn:#1}{A#1}}
\makeatletter
\let\if@nopnote\iffalse % just for the example
\renewcommand{\notenuminnotes}[1]{%
    \edef\tempnumber{#1}%
    \edef\temppgnumber{\thepage}%
    {\normalfont #1.} }
\ExplSyntaxOn
\NewDocumentCommand {\is} { om }{%
    \if@nopnote
    \IfValueTF { #1 } {
        \index[#1]{#2}
    }{
        \index{#2}
    }
    \else
    \hypertarget{pn:\tempnumber}{}%
    \IfValueTF { #1 } {
        \thomkrates_pagenoteindex:nene { #1 } { \tempnumber } { #2 } { \temppgnumber }
    }{
        \thomkrates_pagenoteindex:ene { \tempnumber } { #2 } { \temppgnumber }
    }
    \fi
}
\cs_new_protected:Nn \thomkrates_pagenoteindex:nnnn
{
    \index [#1] { #3 | pn {#2}{#4} }
}
\cs_generate_variant:Nn \thomkrates_pagenoteindex:nnnn { nene }
\cs_new_protected:Nn \thomkrates_pagenoteindex:nnn
{
    \index  { #2 | pn {#1}{#3} }
}
\cs_generate_variant:Nn \thomkrates_pagenoteindex:nnn { ene }
\ExplSyntaxOff
\makeatother

pdflatex 做得很好,多重索引按预期工作,索引条目 pageAnotenum: xAn ... 324A84 例如。

但是当使用 tex4ebook 制作 epub 时,第一个\index{}出现错误...“extra }”

是否有机会获得具有多个索引和页面注释索引的 epub?

謝謝您的支持。

答案1

有几个问题。您遇到的第一个问题是由 TeX4ht 对 Index 和 Imakeidx 包的声明引起的。可以使用此配置文件修复此问题index.4ht

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% index.4ht                             2009-05-21-09:32 %
% Copyright (C) 1999--2009       Eitan M. Gurari         %
%                                                        %
% This work may be distributed and/or modified under the %
% conditions of the LaTeX Project Public License, either %
% version 1.3c of this license or (at your option) any   %
% later version. The latest version of this license is   %
% in                                                     %
%   http://www.latex-project.org/lppl.txt                %
% and version 1.3c or later is part of all distributions %
% of LaTeX version 2005/12/01 or later.                  %
%                                                        %
% This work has the LPPL maintenance status "maintained".%
%                                                        %
% This Current Maintainer of this work                   %
% is Eitan M. Gurari.                                    %
%                                                        %
% If you modify this program your changing its signature %
% with a directive of the following form will be         %
% appreciated.                                           %
%            \message{signature}                         %
%                                                        %
%                             [email protected]  %
%                 http://www.cse.ohio-state.edu/~gurari  %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\immediate\write-1{version 2009-05-21-09:32}

\ifx \a:makeindex\:UnDef
   \NewConfigure{makeindex}{1} \Configure{makeindex}{}
\fi

\expandafter\ifx \csname @vwritefile\endcsname\relax
  \pend:defII\@wrindex{\warn:idx{##1}\title:chs{\html:addr
   \hbox{\Link-{}{x\last:haddr}\EndLink}}{}%
   \edef\:temp{\write\@auxout{%
      \string\@writefile{##1}{\expandafter\string\a:idxmake{\RefFileNumber
         \FileNumber}{\title:chs{x\last:haddr}{\cur:th
         \:currentlabel}}{\a:makeindex}}}}\:temp
}
\pend:def\printindex{\def\indexname{\the\@nameuse{idxtitle@\@indextype}}}
\def\warn:idx#1{%
  \expandafter\ifx \csname #1warn:idx\endcsname\relax
     \expandafter\global
         \expandafter\let \csname #1warn:idx\endcsname\def
     \:warning{If not done so, the index is to be processed by
      ^^J\space\space tex '\string\def\string\filename
         {{\jobname}{\expandafter
\expandafter\expandafter\idx:extI \csname idx@#1\endcsname//%
}{4dx}%
          {\expandafter
\expandafter\expandafter\idx:extII \csname idx@#1\endcsname//%
}} \noexpand
             \input\space idxmake.4ht'
      ^^J\space\space  makeindex -o
         \jobname.\expandafter
\expandafter\expandafter\idx:extII \csname idx@#1\endcsname//%
\space \jobname.4dx
      ^^Jinstead of
      ^^J\space\space  makeindex -o
         \jobname.\expandafter
\expandafter\expandafter\idx:extII \csname idx@#1\endcsname//%
\space
         \jobname.\expandafter
\expandafter\expandafter\idx:extI \csname idx@#1\endcsname//%
%
      ^^JOn some platforms, the quotation marks ' should be
      ^^J      replaced by double quotation marks " or eliminated.
     }%
     {\Configure{Needs}{File: #1.4idx}\Needs{}}%
  \fi}
\bgroup
\@ifpackageloaded{imakeidx}{
   \expandafter\gdef\csname idx:extI\endcsname#1{#1}
   \expandafter\gdef\csname idx:extII\endcsname#1{#1}
}{
   \catcode`\:=12
   \expandafter\gdef\csname idx:extI\endcsname#1:#2//{#1}
   \expandafter\gdef\csname idx:extII\endcsname#1:#2:#3//{#2}
}
\egroup


\else
  \pend:defII\@wrindex{\warn:idx{##1}\title:chs{\html:addr
   \hbox{\Link-{}{x\last:haddr}\EndLink}}{}%
   \edef\:temp{\write\@auxout{%
      \string\@vwritefile{##1}{\expandafter\string\a:idxmake{\RefFileNumber
         \FileNumber}{\title:chs{x\last:haddr}{\cur:th
         \:currentlabel}}{\a:makeindex}}}}\:temp
}

\fi
\Hinput{index}
\endinput

然后,您需要一个构建文件来创建索引build.lua

Make:htlatex {} 
Make:makeindex {} 
Make:htlatex {}

请注意,该Make:makeindex{}命令会自动拆分索引,因此它将同时创建NamesSomething索引。

不幸的是,您的\pn宏将破坏 Make4ht 中的索引支持。您将需要更新的make4ht-indexing.lua文件:


local M = {}
local log = logging.new "indexing"

-- Handle accented characters in files created with \usepackage[utf]{inputenc}
-- this code was originally part of https://github.com/michal-h21/iec2utf/
local enc = {}

local licrs = {}
local codepoint2utf = unicode.utf8.char 
local used_encodings = {}

-- load inputenc encoding file
local function load_encfiles(f)
    local file= io.open(f,"r")
    local encodings = file:read("*all")
    file:close()
    for codepoint, licr in encodings:gmatch('DeclareUnicodeCharacter(%b{})(%b{})') do
        local codepoint = codepoint2utf(tonumber(codepoint:sub(2,-2),16))
        local licr= licr:sub(2,-2):gsub('@tabacckludge','')
        licrs[licr] = codepoint
    end
end

local function sanitize_licr(l)
    return l:gsub(" (.)",function(s) if s:match("[%a]") then return " "..s else return s end end):sub(2,-2)
end

local load_enc = function(enc)
  -- use default encodings if used doesn't provide one
  enc = enc or  {"T1","T2A","T2B","T2C","T3","T5", "LGR"}
    for _,e in pairs(enc) do
        local filename = e:lower() .. "enc.dfu"
    -- don't process an enc file multiple times
    if not used_encodings[filename] then
      local dfufile = kpse.find_file(filename)
      if dfufile then
        load_encfiles(dfufile)
      end
    end
    used_encodings[filename] = true
    end
end



local cache = {}

local get_utf8 = function(input)
    local output = input:gsub('\\IeC[%s]*(%b{})',function(iec)
    -- remove \protect commands 
    local iec = iec:gsub("\\protect%s*", "")
        local code = cache[iec] or licrs[sanitize_licr(iec)] or '\\IeC '..iec
        -- print(iec, code)
        cache[iec] = code
        return code
    end)
    return output
end


-- parse the idx file produced by tex4ht
-- it replaces the document page numbers by index entry number
-- each index entry can then link to place in the HTML file where the
-- \index command had been used

local parse_idx = function(content)
  -- index entry number
  local current_entry = 0
  -- map between index entry number and corresponding HTML file and destination
  local map = {}
  local buffer = {}

  for line in content:gmatch("([^\n]+)") do
    if line:match("^\\beforeentry") then
      -- increment index entry number
      current_entry = current_entry + 1
      local file, dest = line:match("\\beforeentry%s*{(.-)}{(.-)}")
      map[current_entry] = {file = file, dest = dest}
    elseif line:match("^\\indexentry") then
      -- replace the page number with the current
      -- index entry number
      local result = line:gsub("%b{}$", "{"..current_entry .."}")
      buffer[#buffer+1] = get_utf8(result)
    else
      buffer[#buffer+1] = line
    end
  end
  -- return table with page to dest map and updated idx file
  return {map = map, idx = table.concat(buffer, "\n")}
end

-- replace page numbers in the ind file with hyperlinks
local fix_idx_pages = function(content, idxobj)
  local buffer = {}
  local entries = idxobj.map
  for  line in content:gmatch("([^\n]+)")  do
    local line = line:gsub("(%s*\\%a+.-%,)(.+)$", function(start,rest)
      -- there is a problem when index term itself contains numbers, like Bible verses (1:2),
      -- because they will be detected as page numbers too. I cannot find a good solution 
      -- that wouldn't break something else.
      -- There can be also commands with numbers in braces. These numbers in braces will be ignored, 
      -- as they may be not page numbers
      return start .. rest:gsub("(%{?%d+%}?)", function(page)
        local entry = entries[tonumber(page)]
        if entry then
          -- construct link to the index entry
          return "\\Link[" .. entry.file .."]{".. entry.dest .."}{}" .. page .."\\EndLink{}" 
        else
          return page
        end
      end)
    end)
    buffer[#buffer+1] = line 
  end
  return table.concat(buffer, "\n")
end

-- prepare the .idx file produced by tex4ht
-- for use with Xindy or Makeindex
local prepare_idx = function(filename)
  local f = io.open(filename, "r")
  if not f then return nil, "Cannot open file :".. tostring(filename) end
  local content = f:read("*all")
  local idx = parse_idx(content)
  local idxname = os.tmpname()
  local f = io.open(idxname, "w")
  f:write(idx.idx)
  f:close()
  -- return the object with mapping between dummy page numbers 
  -- and link destinations in the files, and the temporary .idx file
  -- these can be used for the processing with the index processor
  return idx, idxname
end

-- add links to a index file
local process_index = function(indname, idx)
  local f = io.open(indname,  "r")
  if not f then return  nil, "Cannot open .ind file: " .. tostring(indname) end
  local content = f:read("*all")
  f:close()

  local newcontent = fix_idx_pages(content, idx)
  local f = io.open(indname,"w")
  f:write(newcontent)
  f:close()
  return true
end

local get_idxname = function(par)
  return par.idxfile or par.input .. ".idx"
end

local prepare_tmp_idx = function(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")
  load_enc()
  -- save hyperlinks and clean the .idx file
  local idxdata, newidxfile = prepare_idx(par.idxfile)
  if not idxdata then
    -- if the prepare_idx function returns nil, the second reuturned value contains error msg
    return nil, newidxfile
  end
  return  newidxfile, idxdata
end


local splitindex = function(par)
  local files = {}
  local idxfiles = {}
  local buffer 
  local idxfile = get_idxname(par)
  if not idxfile or not mkutils.file_exists(idxfile) then return nil, "Cannot load idx file " .. (idxfile or "''") end
  for line in io.lines(idxfile) do
    local file = line:match("indexentry%[(.-)%]")
    if file then
      -- generate idx name for the current output file
      file =  par.input .. "-" ..file .. ".idx"
      local current = files[file] or {}
      -- remove file name from the index entry
      local indexentry = line:gsub("indexentry%[.-%]", "indexentry")
      -- save the index entry and preseding line to the current buffer
      table.insert(current, buffer)
      table.insert(current, indexentry)
      files[file] = current
    end
    -- 
    buffer = line
  end
  -- save idx files
  for filename, contents in pairs(files) do
    log:info("Saving split index file: " .. filename)
    idxfiles[#idxfiles+1] = filename
    local f = io.open(filename, "w")
    f:write(table.concat(contents, "\n"))
    f:close()
  end
  return idxfiles
end

local function run_indexing_command (command, par)
  -- detect command name from the command. It will be the first word
  local cmd_name = command:match("^[%a]+") or "indexing"
  local xindylog  = logging.new(cmd_name)
  -- support split index
  local subindexes = splitindex(par) or {}
  if #subindexes > 0 then
    -- call the command again on all files produced by splitindex
    for _, subindex in ipairs(subindexes) do
      -- make copy of the parameters
      local t = {}
      for k,v in pairs(par) do t[k] = v end
      t.idxfile = subindex
      run_indexing_command(command, t)
    end
    return nil
  end
  local newidxfile, idxdata = prepare_tmp_idx(par)
  if not newidxfile then
    -- the idxdata will contain error message in the case of error
    xindylog:warning(idxdata)
    return false
  end
  par.newidxfile = newidxfile
  xindylog:debug("Prepared temporary idx file: ", newidxfile)
  -- prepare modules
  local xindy_call = command % par
  xindylog:info(xindy_call)
  local status = mkutils.execute(xindy_call)
  -- insert correct links to the index
  local status, msg = process_index(par.indfile, idxdata)
  if not status then xindylog:warning(msg) end
  -- remove the temporary idx file
  os.remove(newidxfile)
  -- null the indfile, it is necessary in order to support
  -- multiple indices
  par.indfile = nil
end


M.get_utf8 = get_utf8
M.load_enc = load_enc
M.parse_idx = parse_idx
M.fix_idx_pages = fix_idx_pages
M.prepare_idx = prepare_idx
M.process_index = process_index
M.prepare_tmp_idx = prepare_tmp_idx
M.run_indexing_command = run_indexing_command
return M

最后,您的宏pagenote-index-usageB.tex可以向单个索引条目添加两个宏,结果如下:

\indexentry{Emph index@\emph  {Emph index}|see{Index inside second pagenote}|pn{2}{4}}{4} 

makeindex不会喜欢该条目并完全忽略它。仅当索引条目尚未包含时,才需要|pn在宏中添加宏。这是一个修复版本:\thomkrates_pagenoteindex:nnnn|

\newcommand\pn[2]{\hyperpage{#2}\hyperlink{pn:#1}{A#1}}
\makeatletter
\let\if@nopnote\iffalse % just for the example
\renewcommand{\notenuminnotes}[1]{%
    \edef\tempnumber{#1}%
    \edef\temppgnumber{\thepage}%
    {\normalfont #1.} }
\ExplSyntaxOn
\NewDocumentCommand {\is} { om }{%
    \if@nopnote
    \IfValueTF { #1 } {
        \index[#1]{#2}
    }{
        \index{#2}
    }
    \else
    \hypertarget{pn:\tempnumber}{}%
    \IfValueTF { #1 } {
        \thomkrates_pagenoteindex:nene { #1 } { \tempnumber } { #2 } { \temppgnumber }
    }{
        \thomkrates_pagenoteindex:ene { \tempnumber } { #2 } { \temppgnumber }
    }
    \fi
}
\cs_new_protected:Nn \thomkrates_pagenoteindex:nnnn
{
  % don't add |pn if the index entry already contains |
  \tl_if_in:nnTF{#3}{|}{
    \index [#1] { #3 }
  }{%
    \index [#1] { #3 | pn {#2}{#4} }
  }
}
\cs_generate_variant:Nn \thomkrates_pagenoteindex:nnnn { nene }
\cs_new_protected:Nn \thomkrates_pagenoteindex:nnn
{
    \index  { #2 | pn {#1}{#3} }
}
\cs_generate_variant:Nn \thomkrates_pagenoteindex:nnn { ene }
\ExplSyntaxOff
\makeatother

经过这些更改,你的文件应该可以编译:

在此处输入图片描述

相关内容