概念证明

概念证明

是否有任何软件包可以自动突出显示输出 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.widthsp单位)来获得正确的下划线。在下面的例子中,每个字母都单独加下划线,可以改进为每个单词画一条线。我们保留这个想法,以便对代码进行进一步的实验。这需要在函数的第一次运行中存储宽度,并在 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}

概念证明的一个例子

相关内容