是否有任何软件包可以自动突出显示输出 PDF 中不可连字符的单词?即不在连字符单词列表中的单词。例如:
我正在使用 polyglossia 包。非常感谢!
答案1
概念证明
我们检查了一些常见的软件包,例如乌莱姆(唐纳德·阿瑟诺)和灵魂(Melchior Franz),之后我们将注意力转移到显示连字符,lua-检查连字符和lua-visual-调试Patrick Gundlach 创建的软件包。不幸的是,OP 需要的与这些软件包提供的内容相反的东西。
我们的工作是从分析和学习开始的此代码来自 TeX.SX(也可以看看http://wiki.luatex.org/index.php/Show_the_hyphenation_points)作者:Patrick Gundlach。我们始终得到LuaTeX 参考手册,我们注意到已定义节点类型(请参阅手册中的第 8 章节点第一页;第 169 页及以上)。我们可以在以下页面中阅读有关其字段的信息。
我们特别需要这些类型:
- hlist (0),水平列表,
- vlist (1),垂直列表,
- 圆盘(7),一个单词可以连字符的地方,
- whasit (8),适合新节点创建的类型,
- 胶水 (10),空格,
- 字距 (11)、字距调整,以及
- 字形(37),此类工作的关键节点类型。
我们的任务是遍历所有节点,收集并存储它们的 ID 到 Lua 表中,在文本级别进行一些基本分析,然后对结果进行一些基本突出显示(在此示例中,我们对每个字形使用了下划线)。
我们传递了最少数量的字母,这些字母必须包含要突出显示的单词。单个单词由字母、数字、标点符号等组成。我们认为这对于这个概念证明来说应该足够了。
显示连字符的原始代码使用递归算法遍历节点树。我们需要两次传递,因此我们将该函数(show_hyph
)包装到另一个函数(towork
)。分析在showme
函数中完成。我们使用head
用户数据,我们可以使用该函数打印字段getmetatable
。它在代码中被注释掉,并打印有关节点的其他信息。
文本分析包含几个条件,主要来自:
- ID 0 或 ID 10 后跟 ID 37(另外,该单词不能已用连字符连接),这是单词的开头。它是一个新行或一个空格,后跟一个字形。
- ID 7 意味着可能存在光盘,并且该词对我们来说不感兴趣。
- ID 11(字距调整)或 ID 37(字形)是单词的一部分,我们将该部分包括在内以供进一步分析。
- ID 37 后面跟着 ID 10,这是一个单词的结尾。
我们将所有信息存储在 Lua 表中。我们需要两个变量,分别用于单词长度、显示我们使用的单词malsizetrue
(字形+字距调整节点)、与我们使用的用户交互malsize
(仅字形)。一开始用这些术语来思考单词很奇怪,但这是必要的。
我们使用head.width
(sp
单位)来获得正确的下划线。在下面的例子中,每个字母都单独加下划线,可以改进为每个单词画一条线。我们保留这个想法,以便对代码进行进一步的实验。这需要在函数的第一次运行中存储宽度,并在 Lua 级别的第二次运行中使用它们。
在以下示例中,我们使用了一些基本文本,并将每个单词的最小字母数设置为 1 到 8。我们附上了代码和结果预览(8 页)。它在此示例中按预期工作,但我猜此代码片段需要更多测试。
我们跑lualatex mal-hyph.tex
。
% lualatex mal-hyph.tex
\documentclass[a4paper]{article}
\pagestyle{empty}
\usepackage{luacode} % the core package in LuaLaTeX, recommended is luatextra
\usepackage[english]{babel} % loading some hyphenation rules
\usepackage{kantlipsum} % loading some text
\usepackage{fontspec} % loading some font
\begin{luacode*}
-- This is the core function for callback...
function towork(head)
-- This inner function is recursive...
function show_hyph(head)
while head do
-- If we want to see userdata of >head<...
-- for i,j in pairs(getmetatable(head)) do
-- print(i,j)
-- end
-- If we need to get nodes ID..., this is the way.
--[=[ io.write(head.id.." ")
if head.id ==10 then io.write(" ") end
if head.id == 37 then
io.write(unicode.utf8.char(head.char).."; ")
end
--]=] -- be silent
if head.id == 0 or head.id == 1 then
show_hyph(head.head) -- the core of recursion
end -- if, head.id, 0 (hlist) or 1 (vlist)
counter=counter+1 -- number of nodes
malglyphs[counter]=head.id -- saving IDs for later analysis
if run then -- We can add nodes after analysis...
if maldraw[counter]==1 then -- These letters belong to unhyphenateable words.
local n = node.new("whatsit","pdf_literal") -- Underline them, please,
n.mode = 0
if head.width then dista=head.width/65535 else dista=0 end -- with proper width,
distb=0
n.data = "q 1 w 0 0 1 RG "..-dista.." -2 m "..distb.." -2 l S Q" -- with some common line.
n.next = head.next -- Add line to the node tree.
n.prev = head
head.next = n
head = n
end -- if we are done with lines.
if head.id == 7 and run then -- a divis, this is an original example, addition of disc
local n = node.new("whatsit","pdf_literal")
n.mode = 0
n.data = "q 0.9 w 1 .1 .1 RG 0 2 m 0 7 l S Q" -- an original setting was: q 0.3 w 0 2 m 0 7 l S Q
n.next = head.next
n.prev = head
head.next = n
head = n
end -- if, head.id==7
end -- if, run
head = head.next -- go to the next node
end -- while, node three
end -- function, show_hyph
-- This function analyses ID of nodes.
function showme()
-- Initialize some variables.
malwrite=0 -- Can I store a unhyphenateable word?
maldraw={} -- Table with 1s for storing information.
malsize=0 -- A size of the last word (only letters, ID 37).
malsizetrue=0 -- A size of the last word (letters+kerning, IDs 11+37).
broken=0 -- Is a word already hyphenated?
for malcounter=1,#malglyphs do -- Let's analyse all nodes.
-- Print some letters from actual position backward.
-- print(malglyphs[malcounter-2], malglyphs[malcounter-1], malglyphs[malcounter])
-- Are we at the beginning of the word?
if (malglyphs[malcounter-1]==0 or malglyphs[malcounter-1]==10) and malglyphs[malcounter]==37 and broken~=1 then malwrite=1; malsize=0; malsizetrue=0 end
-- Are we at the end of the word? Can we modify data in our Lua table?
if malglyphs[malcounter-1]==37 and (malglyphs[malcounter]==10 or malglyphs[malcounter]==12) then
if malwrite==1 and malsize>=malmax then -- used to be: malsize>0
for malback=1,malsizetrue do -- set letters of unhyphenated word for underlining/highlighting, plus kerning
maldraw[malcounter-malback]=1 -- mark them letter by letter
end -- for, malback
end -- if, malwrite
end -- if, malglyphs...
-- Are we in the middle of the hyphenated word?
if malglyphs[malcounter]==7 then broken=1 end -- Yes, we are!
if malglyphs[malcounter]==37 then broken=0 end -- No, at least not at the beginning of the line!
-- Is this a node with letter or kerning in some word?
if malglyphs[malcounter]==37 or malglyphs[malcounter]==11 then
malsizetrue=malsizetrue+1 -- letters+kernings
if malglyphs[malcounter]==37 then malsize=malsize+1 end -- Only letters.
else
malwrite=0 -- Stop analysing actual word, it has no meaning anymore.
end -- stop searching
end -- for, malcounter
-- If we need some information from the Lua table...
--[[for allchars=1,#malglyphs do
if maldraw then print(allchars, maldraw[allchars]) end
end -- for, malheader
]]
end -- function, showme
-- This is the first run, we need data about nodes.
malglyphs={}; run=nil; counter=0
show_hyph(head)
showme()
-- This is the second run, we will highlight letters (and also kerning with an invisible pen :-) ).
malglyphs={}; run=1; counter=0
show_hyph(head)
showme()
return true -- Let's finish our work and send data back to a callback.
end -- major function, towork()
\end{luacode*}
% Some TeX commands for general use...
% A minimum of letters required in an unhyphenateable word to be highlighted.
\def\malactivateset#1{
\luaexec{
print("malmax "..#1)
malmax=tonumber(#1) or 0
}}
% This command activates a callback.
\def\malactivate{
\directlua{luatexbase.add_to_callback("post_linebreak_filter",towork,"towork")}
} % End of \activateme...
% This command deactivates a callback.
\def\maldeactivate{
\directlua{luatexbase.remove_from_callback("post_linebreak_filter","towork")}
} % End of \deactivateme...
% This text is an example for TeX.SX.
\def\maltext{\newpage
Minimum of required letters: \the\malletters.\par
Hello, the World!\par
Here is some text without meaning.\par
\kant[1]\par
} % End of \maltext...
% Small changes in paper mirror...
\rightskip=7cm
\parindent=0pt
\begin{document}
% A small example...
\malactivate % This command activates the algorithm...
\newcount\malletters
\malletters=0 % Start with 0+1 letters, a common minimum.
\loop
\advance\malletters by 1 % Try more letters.
\malactivateset{\the\malletters} % Pass No. of letters to Lua.
\maltext \relax % Text to be tested.
\ifnum\malletters<8 \repeat % Show us 8 pages.
\maldeactivate % This command deactivates the algorithm...
\end{document}