概念證明

概念證明

是否有任何軟體包可以自動突出顯示輸出 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.widthsp單位)來獲得適當的底線。在下面的範例中,每個字母都單獨加下劃線,可以改進為每個單字畫一條線。我們將保留這個想法,以便對程式碼進行進一步的實驗。這需要在函數的第一次運行中儲存寬度,並在 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}

概念證明的範例

相關內容