我正在使用 TeX4ht 制作文档的 html 和 epub 版本,这些版本也会被处理为 pdf。我不清楚如何在 html 和 epub 中包含某种与 pdf(“主”)文档的页码相关的可视页面引用。事实上,查看者无法知道哪个 pdf 页面与他在 html 文本中的位置相对应。
LaTeX/pdf 版本中有几个内部页面引用(即\pageref{xxx}
),但确实可以传递到 html/epub 版本,例如“参见第 46 页”。在 html 中单击时,链接确实会转到文档中 pdf 版本中的第 46 页区域(我猜这是预期结果,因为链接实际上是指向嵌入的锚点,而不是指向特定页面),但 html 文件中没有数据来指示 LaTeX/pdf 版本中的分页符在哪里。
TeX4ht 中是否有一些选项可以设置,让其自动在 html 中包含页码代码(例如,“[*46]”)。或者是否有命令可以包含在 LaTeX 文件本身中,以便它插入将包含在 TeX4ht 获得的 html 中的页面标记?
答案1
我认为页码在电子书中也很重要。在很多书籍中,尤其是人文、哲学等,有些章节没有章节,长度大约有 40、50 页。在这种情况下,页码确实很有必要。
书籍原始布局的页码很重要的其他例子是盲文书,其中原始页码与盲文文档的页码一起使用。
tex4ht 无法访问原始文档的页码,因为它使用 dvi 文件的特殊格式,其中的页面与 pdf 布局不对应。
可以使用一些预处理工具,例如从 pdf 文件中提取每页的最后一行并不难,但在 tex 文件中找到该文本就很难,因为它可以从宏中生成。
所以我认为唯一的方法是使用 luatex,这样我们就可以访问页面组装后的页面内容,或者在段落换行之前访问它们。缺点是,tex4ht 还没有很好地支持 luatex,但我希望在不久的将来它会更好。见我的这个答案。
目前,我在办公室的 Linux 桌面上有 htlualatex(用于 luatex 的 htlatex)的工作实例,但在家里的 Windows 桌面上没有,所以我无法测试代码的 html 生成,只能使用不同大小的 pdf。
我的想法是计算 pdf 文件中所有字形的校验和,并在每个分页符处保存它们的值。然后,当我们想在具有不同布局的文件中插入分页符标记时,加载保存的校验和,计算新文件的校验和,并在它们匹配的地方插入标记。我知道这不是一种可靠的方法,而且像数字、方程式等这样的思维方式可能会破坏它,所以如果有人知道更好的算法,我会很高兴。
现在有一个示例:
\documentclass{book}
\usepackage{lipsum}
\usepackage[a4paper]{geometry}
\usepackage[]{milestone}
\begin{document}
\title{Milestone sample}
\author{Michal}
\maketitle
\chapter{First}
\section{Section}
\lipsum[2-12]
\subsection{Subsection}
\lipsum[2]
\chapter{Next chapter}
\lipsum[3-20]
\end{document}
现在我们可以设置不同的页面布局并插入页码标记\usepackage[insert]{milestone}
:
\documentclass{book}
\usepackage{lipsum}
\usepackage[a5paper]{geometry}
\usepackage[insert]{milestone}
\begin{document}
\title{Milestone sample}
\author{Michal}
\maketitle
\chapter{First}
\section{Section}
\lipsum[2-12]
\subsection{Subsection}
\lipsum[2]
\chapter{Next chapter}
\lipsum[3-20]
\end{document}
这是文件milestone.sty
:
\ProvidesPackage{milestone}
\RequirePackage{luacode,luatexbase,etoolbox,kvoptions}
\DeclareBoolOption{insert}
\ProcessKeyvalOptions*
\begin{luacode}
milestone=require("milestone")
\end{luacode}
\ifmilestone@insert
\begin{luacode}
luatexbase.add_to_callback("pre_linebreak_filter",milestone.insertPageBreaks,"Insert")
\end{luacode}
\else
\AfterEndDocument{%
\directlua{milestone.writeMilestones()}
}
\newtoks\oldoutput
\oldoutput=\expandafter{\the\output}
\output{
\directlua{
milestone.pagenumber = "\thepage"
milestone.getPageBreaks(tex.box[255].list)
}
\the\oldoutput
}
\fi
以及 lua 模块milestone.lua
:
--local node,texio,unicode,io,tex,dofile=node,texio,unicode,io,tex,dofile
module("milestone",package.seeall)
-- Variables accessible from the outside
milestonefile = tex.jobname..".mil" -- File, where all milestones are saved
pagenumber="" -- Pagenumbers must be saved in TeX,
-- in the output routine
local glyph = node.id('glyph')
local milchecksum=0
local function calcChecksum(val)
return milchecksum + val
end
-- Item in the current list, where the page break mark should be inserted
function printPageBreak(item,page)
local hi = node.new("glue")
local mynode=mknodes(hi,"//["..page.."]")
local pp=item.next
item.next=hi
node.tail(hi).next=pp
end
local milposition=1
local mcheck=0
function insertPageBreaks(head,group)
t=getMilestones()
for item in node.traverse_id(glyph, head) do
if milposition > #t then break end
if item.id == glyph and item.char ~= 45 then
-- Nodes created with function mknodes() have attribute 224 set to 33,
-- we must skip them
if node.has_attribute(item,224)~=33 then
mcheck=mcheck+item.char
end
if mcheck==t[milposition]["checksum"] then
texio.write_nl("Checksum equality: "..mcheck.." page: "..t[milposition]["page"])
printPageBreak(item,t[milposition]["page"])
milposition=milposition+1
end
end
end
texio.write_nl("Current checksum: "..mcheck)
return head
end
local function processLine(line)
local lcontents=""
for item in node.traverse(line.list) do
if item.id == glyph and item.char ~= 45 then
lcontents = lcontents .. unicode.utf8.char(item.char)
milchecksum= calcChecksum(item.char)
end
end
return lcontents
end
function getPageBreaks(head)
local lcontents = ""
for line in node.traverse_id(node.id("hlist"), head) do
lcontents=processLine(line)
--texio.write_nl(lcontents)
end
addMilestone(pagenumber,milchecksum,lcontents)
return true
end
local miloutput=""
function addMilestone(page,checksum,text)
if text ~= "" then
miloutput = miloutput .. unicode.utf8.format("milestone{\n page=\"%s\",\n checksum=%d,\n text=\"%s\"\n}\n",page,checksum,text)
texio.write_nl(unicode.utf8.format("Page:%s checksum:%d text:%s",page,checksum,text))
end
end
function writeMilestones()
if(miloutput~="") then
local f=io.open(milestonefile,"w")
f:write(miloutput)
f:close()
else
texio.write("Package milestone: No milestones writen to the output")
end
end
function import(name)
local f,e = loadfile(name)
if not f then error(e, 2) end
setfenv(f, getfenv(2))
return f()
end
function getMilestones()
local t={}
local i=1
function milestone(b)
--local function injectElements(el) t[#t+1][el]=b[el] end
t[i]={}
for k,v in pairs(b) do t[i][k] = v end
i=i+1
end
import(milestonefile)
return t
end
function printMilestone(head,current,text)
local message = mknodes("//["..text.."]")
return node.insert_after(head,current,message)
end
function mknodes(head, text )
local current_font = font.current()
local font_parameters = font.getfont(current_font).parameters
local n, last
-- we should insert the paragraph indentation at the beginning
--[[
head = node.new("glue")
head.spec = node.new("glue_spec")
head.spec.width = 5 * 2^16
--]]
last = node.tail(head)
local count=0
for s in string.utfvalues( text ) do
local char = unicode.utf8.char(s)
if unicode.utf8.match(char,"%s") then
-- its a space
n = node.new("glue")
n.spec = node.new("glue_spec")
n.spec.width = font_parameters.space
n.spec.shrink = font_parameters.space_shrink
n.spec.stretch = font_parameters.space_stretch
else -- a glyph
count=count+s
n = node.new("glyph")
n.font = current_font
n.subtype = 1
n.char = s
n.lang = tex.language
n.uchyph = 1
n.left = tex.lefthyphenmin
n.right = tex.righthyphenmin
end
node.set_attribute(n,224,33)
last.next = n
last = n
end
-- just to create the prev pointers for tex.linebreak
node.slide(head)
return head,count
end
local function printNodeList(head)
for ii in node.traverse(head) do
if ii.id == glyph then texio.write(string.char(ii.char))
elseif ii.id == node.id("glue") then texio.write(" ")
end
end
end
答案2
(这更像是一个建议,而不是对你实际问题的回答,但考虑到这个问题很棘手而且目前还没有答案……)
据我所知,TeX4ht 中没有选项可以让您直接将页码输出为锚点,所以,是的,它可能必须在 LaTeX 中完成,而且这很可能并不简单。
面对现实,页码在 HTML 和电子书格式中根本没有意义,因为它们没有“页面”的物理概念。插入上述数字以便读者可以使用文档仿佛PDF 确实不错,但语义上却很尴尬。一种可能的解决方法(即,如果您的编辑器风格或您正在撰写的主题允许)是使用页码以外的其他内容 - 例如,在某些领域,习惯于在整个文档中对段落进行编号,并引用它们而不是页面。如何做到这一点与主题无关,但并不难(计数器、宏和适当的引用命令)。