fontspec 和一些執行字串相關搜尋和取代操作的 lua 程式碼之間的互動不良

fontspec 和一些執行字串相關搜尋和取代操作的 lua 程式碼之間的互動不良

更新2013/05/28: 這塞爾諾利格軟體包現已在 CTAN 上。隨時歡迎評論和批評!如果您想就該包的任何方面與我聯繫,請使用該包使用者指南標題頁底部提供的電子郵件地址。 (相對於下面問題中描述的包的狀態,我已經成功地消除了至少一個錯誤,並且我建議對剩餘的錯誤有更好的解決方法——至少是我所知道的錯誤!)


我正在準備 LuaLaTeX 套件以「正式」發佈到 CTAN,但我需要先消除一些剩餘的錯誤。這個問題中描述的錯誤涉及我的包發生的不正確行為如果fontspec已載入;如果fontspec未加載,則不會出現此處描述的任何問題。顯然,詢問我的包裹的潛在用戶不是載入fontspec不是一個選項。順便說一下,我的系統上LuaTeX版本的標識符字串是“ beta-0.70.2-2012062819”,隨MacTeX2012一起發布。有關完整selnolig包的更多信息,該包可以自動、選擇性地抑製印刷連字,請參閱新軟體包 selnolig,可自動抑製印刷連字

MWE(請參閱下圖,以及下圖提供的程式碼)說明了執行連字抑制失敗的幾個實例,如果 - 並且顯然也除非——fontspec已載入。具體來說,結紮抑制對於以下情況失敗:

  • 緊跟著註釋 ( %) 符號的單字
  • 命令參數中的最後一個詞,例如\footnoteand\section
  • 緊接在環境開始之前的單字,例如enumerateanditemize
  • 語句的最後一個單詞\item,即下一個\item語句和/或環境的結束\end{enumerate/itemize}指令之前的最後一個單字

這些問題的一個共同主題是,如果有問題的單字(加上任何尾隨標點符號)位於某些環境、群組或某些巨集參數的最末尾,就會發生這些問題。在所有情況下,一種「補救措施」是插入空格、空白行或空格加\vphantom{x}[!] 之類的內容。顯然,這些補救措施是不是真正的解決方案,但只是笨拙的駭客,我當然不會考慮要求我的軟體包的用戶實施這些駭客。

我的問題是,那麼:

  • 如何讓我的 lua 程式碼對於fontspecpacakge(或由 載入的某些套件fontspec)所做的任何事情都更加健壯?

  • 有沒有辦法載入其中一個fontspec(或調用的一些套件fontspec)來抑制對我的lua程式碼的干擾?

  • fontspec或者,我是否發現(或加載的一個或多個包fontspec)中存在需要修復的錯誤?

在此輸入影像描述

% !TEX TS-program = lualatex
\documentclass{article}
% If the next line is commented out, everything works fine!
\usepackage{fontspec} 

  \RequirePackage{luatexbase,luacode,expl3}
  % Load lua code
  \directlua{  require("ld-orig.lua")  }  % see below for contents of ld-orig.lua
  % Define the user macro "nolig"
  \providecommand\nolig[2]{ \directlua{
        suppress_liga( "\luatexluaescapestring{#1}",
                       "\luatexluaescapestring{#2}" )}}
  % Provide a ligature suppression rule
  %  (the full package obviously provides many more such macros)
  \nolig{lfful}{lf|ful} % shelfful -> shelf|ful

% Just for this MWE:
   \usepackage[textheight=8cm]{geometry}
   \setlength\parindent{0pt}
   \pagestyle{empty} 

\begin{document}    
Two shelffuls of \TeX-related books: it works!

\bigskip
% word to be de-ligated is followed immediately by % (comment character)
Ligature suppression doesn't work here: shelfful% 

% leaving a space between word and % makes it work even if fontspec is loaded
But it does work in this case: shelfful % 

\bigskip
bad\footnote{This doesn't work: shelfful.} % w/o space 
good\footnote{But this does work: shelfful. \vphantom{x}} % w/ space and \vphantom directive

\bigskip
% Two more problem cases: (i) last word before start of an
% itemize/enumerate environment, (ii) last word of an \item
one shelfful, two shelffuls % no ligature suppression for "shelffuls"
\begin{itemize}
\item shelfful              % no ligature suppression here either
\item shelfful \vphantom{x} % inserting space and \vphantom does the trick...
\end{itemize}

% problem also occurs in arguments of sectioning commands
\section*{sad shelfful}         % again no ligature suppression
\subsection*{happy shelfful }   % adding space at end of argument makes it work!
\end{document}

ld-orig.lua的內容:

--- Credits to Patrick Gundlach, Taco Hoekwater, and Steffen Hildebrandt!
local glyph = node.id('glyph')
local glue = node.id("glue")
local whatsit = node.id("whatsit")
local userdefined

for n,v in pairs(node.whatsits()) do
  if v == 'user_defined' then userdefined = n end
end

local identifier = 123456  -- any unique identifier 
local noliga={}
debug=false  -- default: don't write debugging info to log file

function debug_info(s)
  if debug then
    texio.write_nl(s)
  end
end

local blocknode = node.new(whatsit, userdefined)
blocknode.type = 100
blocknode.user_id = identifier

function process_ligatures(nodes,tail)
  local s={}
  local current_node=nodes
  local build_liga_table =  function(strlen,t)
    local p={}
    for i = 1, strlen do
      p[i]=0
    end
    for k,v in pairs(t) do
      -- debug_info("Match: "..v[3])
      local c= string.find(noliga[v[3]],"|")
      local correction=1
      while c~=nil do
         -- debug_info("Position "..(v[1]+c))
         p[v[1]+c-correction] = 1
         c = string.find(noliga[v[3]],"|",c+1)  
         correction=correction+1
      end   
    end
    -- debug_info("Liga table: "..table.concat(p, ""))
    return p
  end
  local apply_ligatures=function(head,ligatures)
     local i=1
     local hh=head
     local last=node.tail(head)
     for curr in node.traverse_id(glyph,head) do
       if ligatures[i]==1 then
         -- debug_info("Current glyph: "..unicode.utf8.char(curr.char))
         node.insert_before(hh,curr, node.copy(blocknode))
         hh=curr
       end 
       last=curr
       if i==#ligatures then 
         -- debug_info("Leave node list on position: "..i)
         break 
       end
       i=i+1
     end
     if(last~=nil) then
       -- debug_info("Last char: "..unicode.utf8.char(last.char))
     end--]]
  end
  for t in node.traverse(nodes) do
    if t.id==glyph then

      s[#s+1]=string.lower(unicode.utf8.char(t.char))
    elseif t.id== glue then
      local f=string.gsub(table.concat(s,""),"[\\?!,\\.]+","") -- add all interpunction
      local throwliga={}    
      for k, v in pairs(noliga) do
        local count=1
        local match= string.find(f,k)
        while match do
          count=match
          -- debug_info("pattern match: "..f .." - "..k)  
          local n = match + string.len(k)-1
          table.insert(throwliga,{match,n,k})
          match= string.find(f,k,count+1)
        end
      end
      if #throwliga==0 then 
        -- debug_info("No ligature substitution for: "..f)  
      else
        -- debug_info("Do ligature substitution for: "..f)  
        local ligabreaks=build_liga_table(f:len(),throwliga)
        apply_ligatures(current_node,ligabreaks)
      end
      s={}
      current_node=t
    end    
  end
end

function suppress_liga(s,t)
  noliga[s]=t
end

function drop_special_nodes (nodes,tail)
  for t in node.traverse(nodes) do
     if t.id == whatsit and t.subtype == userdefined and t.user_id == identifier then
        node.remove(nodes,t)
        node.free(t)
     end
  end
end

luatexbase.add_to_callback("ligaturing", process_ligatures,"Filter ligatures", 1) 

後記:此帖子中描述的錯誤的解決方案。上面給出的 lua 程式碼中導致該錯誤的關鍵序列是:

  for t in node.traverse(nodes) do
     if t.id==glyph then
        s[#s+1]=string.lower(unicode.utf8.char(t.char))
     elseif t.id==glue then
        ...

修復該錯誤所需要做的就是將此程式碼片段更改為:

  for t in node.traverse(nodes) do
     if t.id==glyph then
        s[#s+1]=string.lower(unicode.utf8.char(t.char))
     end
     if ( t.id==glue or t.next==nil or t.id==kern or t.id==rule ) then
        ...

關鍵在於,需要 selnolig 處理的字元序列可以以其他方式結束,而不僅僅是使用一定量的 (TeX)「黏合」(例如,空格)。如果該單字是正在處理的最後一個項目,則序列結束的另一種方式是,如果它是諸如\section{};之類的命令的參數中的最後一個單字。如果是這種情況,則變數t.next將等於nil。最後,如果使用者手動插入「緊排」或「規則」項,則提供兩個剩餘if條件 -t.id==kern和- 。t.id==rule

此錯誤修復已包含在該軟體包的 0.220 版本中。

答案1

我嘗試分析問題:你ligaturing一遍又一遍地調用,但有時回調似乎沒有任何效果。我想看兩個案例:註腳:

bad\footnote{This doesn't work: shelfful.} % w/o space 
good\footnote{But this does work: shelfful. \vphantom{x}} % w/ space and \vphantom directive

我將查看使用我的小模組傳遞給連接回調的節點列表 可視化節點列表

我將入口點的 lua 程式碼稍微改為process_ligatures()

...
require("viznodelist")
function process_ligatures(nodes,tail)
  counter = counter or 0
  counter = counter + 1
  viznodelist.nodelist_visualize(nodes,string.format("liga%d.gv",counter))

第一個註腳(「壞」)如下所示:

節點列表,完整

詳細資訊(右上角)

在此輸入影像描述

而「好」節點列表如下所示:

在此輸入影像描述

現在看程式碼:

  for t in node.traverse(nodes) do
    if t.id==glyph then

      s[#s+1]=string.lower(unicode.utf8.char(t.char))
    elseif t.id== glue then
    ...
      (process ligatures)
    ...
    end
  end

清楚地顯示只有 aglue激活連字處理。

我建議使用不同類型的循環進行連字處理。

無法啟動之間的差異fontspec如下:當 fontspec 停用時,連字回呼將停用所有連字。你看到的不是命令的效果\nolig,而是一般的「無連字」模式。試試這樣的詞fluffiest fish,你就會看到。啟用 fontspec 後,結果是“始終連字”,除非您用您使用的程式碼封鎖它們。

因此,恐怕連線回調並不是處理這種情況的完美方法。但是,您可以node.ligaturing()在回調開始時調用,然後執行您正在執行的操作。但這可能會幹擾字體規範。

相關內容