Aktualisierung 28.05.2013: DerAbonnierenDas Paket ist jetzt auf dem CTAN. Kommentare und Kritik sind immer willkommen! Wenn Sie mich zu irgendeinem Aspekt des Pakets kontaktieren möchten, verwenden Sie bitte die E-Mail-Adresse, die unten auf der Titelseite des Benutzerhandbuchs des Pakets angegeben ist. (Im Verhältnis zum Status des Pakets, der in der folgenden Frage beschrieben wird, ist es mir gelungen, mindestens einen Fehler zu beheben, und ich schlage bessere Workarounds für die verbleibenden Fehler vor – zumindest für die, die mir bekannt sind!)
Ich bin gerade dabei, ein LuaLaTeX-Paket für die "offizielle" Veröffentlichung im CTAN vorzubereiten, aber ich muss zuerst noch ein paar verbleibende Fehler beheben. Der in dieser Frage beschriebene Fehler betrifft ein fehlerhaftes Verhalten meines Pakets, das auftrittWenndas fontspec
Paket ist geladen; wenn fontspec
es nicht geladen ist, tritt keines der hier beschriebenen Probleme auf. Offensichtlich ist es so, dass ich potenzielle Benutzer meines Pakets frage,nichtzu laden fontspec
ist keine Option. Übrigens ist die Identifikationszeichenfolge der LuaTeX-Version auf meinem System " beta-0.70.2-2012062819
", die mit MacTeX2012 verteilt wird. Weitere Informationen zum vollständigen selnolig
Paket, das automatisch selektive Unterdrückung typografischer Ligaturen durchführt, finden Sie unterNeues Paket, selnolig, das die Unterdrückung typografischer Ligaturen automatisiert.
Das MWE (siehe Bild unten und Code unter dem Bild) zeigt mehrere Fälle, in denen die Ligaturunterdrückung fehlgeschlagen ist, wenn - und anscheinend auchnur wenn-- fontspec
wird geladen. Insbesondere schlägt die Ligaturunterdrückung fehl bei:
- ein Wort, auf das unmittelbar ein Kommentarzeichen (
%
) folgt - das letzte Wort im Argument eines Befehls wie
\footnote
und\section
- ein Wort, das unmittelbar vor dem Beginn einer Umgebung steht, wie z . B.
enumerate
unditemize
- das letzte Wort einer
\item
Anweisung, d. h. das letzte Wort vor der nächsten\item
Anweisung und/oder der schließenden\end{enumerate/itemize}
Direktive der Umgebung
Ein gemeinsames Thema dieser Probleme ist, dass sie auftreten, wenn das fragliche Wort (plus alle nachfolgenden Satzzeichen) ganz am Ende einer Umgebung, Gruppe oder eines Arguments eines Makros steht. In allen Fällen besteht eine Art „Abhilfe“ darin, entweder ein Leerzeichen, eine Leerzeile oder ein Leerzeichen plus etwas wie \vphantom{x}
[! einzufügen. Offensichtlich sind diese Abhilfennichtechte Lösungen, sondern bloß plumpe Hacks, und ich käme sicher nicht auf die Idee, die Benutzer meines Pakets zu bitten, diese Hacks zu implementieren.
Meine Fragensind, dann:
Wie kann ich meinen Lua-Code robuster gegenüber allem machen, was vom
fontspec
Paket (oder einem von geladenen Paketfontspec
) gemacht wird?Gibt es eine Möglichkeit, eines davon
fontspec
(oder einige der von aufgerufenen Paketefontspec
) zu laden, um die Störungen meines Lua-Codes zu unterdrücken?Oder habe ich einen Fehler in
fontspec
(oder einem oder mehreren der von geladenen Paketefontspec
) entdeckt, der sowieso behoben werden muss?
% !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}
Inhalt von 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)
Nachtrag: Die Lösung für den in diesem Beitrag beschriebenen Fehler. Die Tastenfolge im oben angegebenen Lua-Code, die den Fehler verursacht hat, war:
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
...
Um den Fehler zu beheben, musste lediglich dieser Codeausschnitt wie folgt geändert werden:
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
...
Der Punkt ist, dass die Zeichenfolge, die von selnolig verarbeitet werden muss, auch anders enden kann als nur mit einer gewissen Menge (TeX-) „Klebstoff“ (z. B. Leerzeichen). Eine andere Möglichkeit für das Ende der Zeichenfolge besteht darin, dass das Wort das allerletzte zu verarbeitende Element ist, z. B. wenn es das letzte Wort im Argument eines Befehls wie ist \section{}
; wenn dies der Fall ist, t.next
ist die Variable gleich nil
. Schließlich werden die beiden verbleibenden if
Bedingungen – t.id==kern
und t.id==rule
– bereitgestellt, falls ein Benutzer ein „Kern“- oder „Rule“-Element manuell eingefügt hat.
Die Fehlerbehebung ist in Version 0.220 des Pakets integriert.
Antwort1
Ich versuche mal das Problem zu analysieren: Man ruft ligaturing
immer wieder auf, aber manchmal scheint der Rückruf keine Wirkung zu haben. Ich möchte mir zwei Fälle ansehen: die Fußnoten:
bad\footnote{This doesn't work: shelfful.} % w/o space
good\footnote{But this does work: shelfful. \vphantom{x}} % w/ space and \vphantom directive
Ich werde mir die Knotenliste ansehen, die mit meinem kleinen Modul an die Ligaturierungs-Callbacks übergeben wird vizindel.
Ich ändere den Lua-Code am Eingangspunkt leicht zu 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))
Die erste Fußnote („schlecht“) sieht folgendermaßen aus:
mit dem Detail (oben rechts)
während die „gute“ Knotenliste folgendermaßen aussieht:
Schauen wir uns nun den Code an:
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
macht deutlich, dass nur a glue
die Ligaturverarbeitung aktiviert.
Ich würde vorschlagen, für die Ligaturverarbeitung eine andere Art von Schleife zu verwenden.
Der Unterschied zwischen fontspec
aktiviert und nicht aktiviert ist der folgende: Wenn Fontspec deaktiviert ist, deaktiviert der Ligaturierungs-Callback alle Ligaturierungen. Was Sie sehen, ist nicht die Wirkung des Befehls \nolig
, sondern ein allgemeiner „keine Ligatur“-Modus. Versuchen Sie Wörter wie fluffiest fish
und Sie sehen das. Wenn Fontspec aktiviert ist, ist das Ergebnis „immer Ligaturen“, sofern Sie sie nicht mit dem von Ihnen verwendeten Code blockieren.
Ich fürchte, der Ligaturierungs-Callback ist nicht die perfekte Lösung für diese Situation. Sie könnten jedoch node.ligaturing()
zu Beginn des Callbacks aufrufen und dann tun, was Sie tun. Aber das würde wahrscheinlich mit der Fontspec in Konflikt geraten.