是否有任何軟體包可以自動突出顯示輸出 PDF 中不可連字符的單字?即不在連字單字清單中的單字。例如:
我正在使用 Polyglossia 包。非常感謝!
答案1
概念證明
我們檢查了一些常見的包,例如烏萊姆(唐納德·阿瑟諾)和靈魂(Melchior Franz),之後我們將注意力轉移到連字符,lua 檢查連字符和lua-視覺-調試由 Patrick Gundlach 創建的軟體包。不幸的是,OP 需要的與這些軟體包提供的相反。
我們的工作是從分析和學習開始的這段程式碼來自 TeX.SX(也可以看看http://wiki.luatex.org/index.php/Show_the_hyphenation_points)派崔克·岡拉克。我們始終得到以下人士的支持LuaTeX的參考手冊,我們注意到定義的節點類型(請參閱第 8 章節點,第一頁;手冊第 169 頁以上)。我們可以在接下來的幾頁中了解它們的領域。
我們特別需要這些類型:
- hlist (0),水平列表,
- vlist (1),垂直列表,
- 圓盤 (7),可以用連字號連接單字的地方,
- whasit (8),適合建立新節點的類型,
- 膠水 (10)、空白、
- 緊排 (11)、字距調整,以及,
- glyph (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。在這個例子中它按預期工作,但我猜這個片段需要更多的測試。
我們跑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}