2013/05/28更新: のセルノリグパッケージは現在 CTAN に掲載されています。コメントや批評はいつでも歓迎します。パッケージのあらゆる側面について私に連絡したい場合は、パッケージのユーザー ガイドのタイトル ページの下部に記載されている電子メール アドレスを使用してください。(以下の質問で説明されているパッケージの状態と比較して、少なくとも 1 つのバグを解消できました。残りのバグについては、少なくとも私が認識しているものについては、よりよい回避策を提案します。)
私は現在、CTANへの「公式」リリースに向けてLuaLaTeXパッケージを準備中ですが、まずは残っているバグをいくつか修正する必要があります。この質問で説明されているバグは、私のパッケージで発生する不正な動作に関するものです。もしパッケージfontspec
がロードされfontspec
ている場合、ここで説明した問題は発生しません。明らかに、私のパッケージの潜在的なユーザーに尋ねることはないロードするfontspec
オプションはありません。ちなみに、私のシステム上の LuaTeX バージョンの識別子文字列は " beta-0.70.2-2012062819
" で、MacTeX2012 で配布されています。タイポグラフィック リガチャを自動的に選択的に抑制するフル パッケージの詳細についてはselnolig
、活字の合字の抑制を自動化する新しいパッケージ selnolig。
MWE(下の画像と画像の下に提供されたコードを参照)は、合字抑制の実行に失敗したいくつかの例を示しています。また、場合にのみ--fontspec
がロードされます。具体的には、次の場合に合字抑制が失敗します。
%
コメント( )記号が直後に続く単語\footnote
およびなどのコマンドの引数の最後の単語\section
- 環境の開始直前に来る単語、
enumerate
例えばitemize
- 文の最後の単語、つまり次の文や環境の終了指示
\item
の前の最後の単語\item
\end{enumerate/itemize}
これらの問題の共通のテーマは、問題の単語(および末尾の句読点)が、何らかの環境、グループ、またはマクロの引数の最後にある場合に発生することです。すべての場合において、ある種の「解決策」は、スペース、空白行、またはスペースと\vphantom{x}
[!] のようなものを挿入することです。明らかに、これらの解決策はないこれらは本当の解決策ではなく、単なる場違いなハックであり、私のパッケージのユーザーにこれらのハックを実装するように依頼することは絶対に考えません。
私の質問であれば、次のようになります。
fontspec
パッケージ (または によってロードされるパッケージ)によって実行される処理に対して、Lua コードをより堅牢にするにはどうすればよいでしょうかfontspec
?fontspec
どちらか(または によって呼び出されるパッケージの一部fontspec
) をロードして、lua コードへの干渉を抑制する方法はありますか?fontspec
あるいは、 (または によってロードされた 1 つ以上のパッケージ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) 「接着剤」(空白など) 以外の方法で終了できるということです。文字列が終了するもう 1 つの方法は、単語が処理される最後の項目である場合です。たとえば、; などのコマンドの引数の最後の単語である場合、\section{}
その場合、変数t.next
は に等しくなりますnil
。最後に、残りの 2 つのif
条件 --t.id==kern
およびt.id==rule
-- は、ユーザーが手動で「カーニング」または「ルール」項目を挿入した場合に備えて用意されています。
バグ修正はパッケージのバージョン 0.220 に組み込まれています。
答え1
問題を分析してみましょう。 をligaturing
何度も呼び出しても、コールバックが効果を発揮しないことがあります。 2 つのケースを見てみましょう。脚注:
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
glue
a のみが合字処理をアクティブにすることを明確にします。
合字処理には別の種類のループを使用することをお勧めします。
有効かどうかの違いはfontspec
次のとおりです。fontspec が無効になっている場合、合字コールバックはすべての合字を無効にします。表示されるのは、コマンドの効果ではなく\nolig
、一般的な「合字なし」モードです。 などの単語を試してfluffiest fish
、それを確認してください。fontspec が有効になっている場合、使用するコードで合字をブロックしない限り、結果は「常に合字」になります。
したがって、残念ながら、合字コールバックは状況に対処するための完璧な方法ではありません。ただし、node.ligaturing()
コールバックの先頭で呼び出してから、実行していることを実行することはできます。ただし、これはおそらく fontspec に干渉するでしょう。