Ich habe erfahren, dass \parbox
es fünf Argumente hat, nämlich: \parbox[<align>][<height>][<inner-align>]{<width>}{<text>}
. Also konstruiere ich meine eigene \myparbox, in die ich \sloppy\setlength\parfillskip{0pt}
vor #5 einfüge. Aber es schlägt fehl. Was ist der Grund und wie kann ich \myparbox neu definieren?
MWE:
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\myparbox}{ooomm}{%
\parbox[#1][#2][#3]{#4}{\sloppy\setlength\parfillskip{0pt}#5}
}
\begin{document}
AAA\fbox{\parbox[][][]{4em}{aa bb cc dd ee ff}}AAA\\% parbox typesets nothing. why?
BBB\fbox{\myparbox{4em}{aa bb cc dd ee ff}}BBB% This fails to compile
\end{document}
BEARBEITEN:
\documentclass{article}
\usepackage{xparse}
\let\oldparbox\parbox
\RenewDocumentCommand{\parbox}{sO{c}oO{t}mm}{%
\IfBooleanTF{#1}
{%
\IfNoValueTF{#3}
{\oldparbox[#2]{#5}{\sloppy\setlength\parfillskip{0pt}#6}}
{\oldparbox[#2][#3][#4]{#5}{\sloppy\setlength\parfillskip{0pt}#6}}
}
{%
\IfNoValueTF{#3}
{\oldparbox[#2]{#5}{#6}}
{\oldparbox[#2][#3][#4]{#5}{#6}}
}
}
\begin{document}\the\fboxsep
AAA\fbox{\parbox[t]{8em}{aa bb cc dd ee ff gg hh ii}}AAA\\% parbox typesets nothing why?.
BBB\fbox{\parbox*{6em}{aa bb cc dd ee ff}}BBB% This fails to compile
\end{document}
Antwort1
Wie in den Kommentaren erwähnt, []
muss ein leeres optionales Argument nicht unbedingt gleichbedeutend damit sein, überhaupt kein optionales Argument anzugeben. Ob das Nichtangeben des Arguments gleichbedeutend mit der Übergabe eines bestimmten Werts ist, muss anhand der Dokumentation oder Implementierung des Befehls überprüft werden.
Im Beispiel wird es noch schlimmer, weil xparse
das optionale o
Argument von tatsächlich den speziell markierten Wert enthält, -NoValue-
wenn das entsprechende optionale Argument nicht angegeben wurde. Sie können und sollten das Vorhandensein eines Wertes mit testen \IfNoValueTF
(wie vorgeschlagen vonAbonnieren's Kommentar).
Das ist eine sehr praktikable Lösung, wenn Sie nur mit einem optionalen Argument arbeiten müssen, aber es wird kompliziert, wenn die Zahl der Argumente zunimmt.
In diesem Fall können Sie als speziellen Marker \parbox
herausfinden, dass die Standardargumente sind c
, und wenn Sie die Definition in nachschlagen .\relax
s
sourc2e.pdf
Sie könnten es also versuchen
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\myparbox}{O{c}O{\relax}O{s}mm}{%
\parbox[#1][#2][#3]{#4}{\sloppy\setlength\parfillskip{0pt}#5}%
}
\begin{document}
BBB\fbox{\parbox{4em}{\sloppy\setlength\parfillskip{0pt}aa bb cc dd ee ff}}BBB
BBB\fbox{\myparbox{4em}{aa bb cc dd ee ff}}BBB
\end{document}
Normalerweise würde ich versuchen, Probleme wie dieses zu umgehen, bei denen ich die standardmäßigen optionalen Argumentwerte/das Verhalten kennen muss und/oder eine große Anzahl von \IfNoValueTF
Tests durchführen muss, um die Argumente richtig zu erhalten, da die erste Methode fragil und die zweite sehr ausführlich und repetitiv erscheint. Ein Ansatz ähnlich wieMarijns Antwortkönnte eine Alternative sein, erfordert aber zumindest in diesem Fall auch genaue Kenntnisse der Definition von \parbox
.
bearbeiten: Ich habe gerade erst die neue Version der Frage gesehen. \let
reicht für robuste Befehle mit optionalen Argumenten nicht aus:Wann soll \LetLtxMacro verwendet werden?. Ich würde außerdem dringend davon abraten, grundlegende Befehle wie neu zu definieren, \parbox
selbst wenn die Implementierung abwärtskompatibel sein könnte. Ein neuer Name ist viel sicherer.
Antwort2
Alternativ können Sie xpatch
den zusätzlichen Code an der richtigen Stelle im Befehl einfügen, ohne Variablen übergeben zu müssen. Der \parbox
Befehl ruft den internen \@iiiparbox
Befehl auf, der den eigentlichen Inhalt verarbeitet, daher ist dieser interne Befehl derjenige, der gepatcht werden sollte. MWE:
\documentclass{article}
\usepackage{xpatch}
\begin{document}
\makeatletter
\xpatchcmd{\@iiiparbox}{#5}{\sloppy\setlength\parfillskip{0pt}#5}{}{}
\makeatother
AAA\fbox{\parbox{4em}{aa bb cc dd ee ff}}AAA
\end{document}
Beachten Sie, dass dies das Verhalten aller Parboxen ändert. Wenn Sie einen benutzerdefinierten Befehl mit Blocksatzausrichtung wünschen und gleichzeitig den regulären Befehl beibehalten möchten, können Sie den benutzerdefinierten Befehl mithilfe einer Kopie des \@iiiparbox
Befehls definieren und die Kopie anstelle des Originals patchen. MWE:
\documentclass{article}
\usepackage{xpatch}
\begin{document}
\makeatletter
\def\myparbox{\@ifnextchar [\@iparbox {\myiiiparbox c\relax [s]}}
\let\myiiiparbox\@iiiparbox
\xpatchcmd{\myiiiparbox}{#5}{\sloppy\setlength\parfillskip{0pt}#5}{}{}
\makeatother
AAA\fbox{\parbox{4em}{aa bb cc dd ee ff}}AAA
BBB\fbox{\myparbox{4em}{aa bb cc dd ee ff}}BBB
\end{document}
Ergebnis:
Antwort3
Die optionalen Argumente \parbox
müssen bestimmte Token enthalten, falls vorhanden. Der Standardwert für das erste ist c
, sodass wir uns einige Überprüfungen auf das Vorhandensein der optionalen Argumente sparen können. Man muss mit dem letzten beginnen.
Das erste optionale Argument sollte c
(Standard) t
oder sein b
, das zweite sollte eine Länge sein, das letzte sollte c
, oder t
sein (wenn nicht angegeben, wird das erste verwendet).b
s
Beachten Sie, dass das letzte Argument \myparbox
mit gekennzeichnet werden sollte +m
, um verschiedene Absätze eingeben zu können. Das \deliverparbox
Makro wird verwendet, um Code-Duplikationen zu minimieren.
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\myparbox}{O{c}oom+m}{%
\IfNoValueTF{#3}
{%
\IfNoValueTF{#2}
{%
\parbox[#1]{#4}{\deliverparbox{#5}}%
}%
{%
\parbox[#1][#2]{#4}{\deliverparbox{#5}}%
}%
}%
{%
\parbox[#1][#2][#3]{#4}{\deliverparbox{#5}}%
}
}
\NewDocumentCommand{\deliverparbox}{+m}{%
\sloppy\setlength\parfillskip{0pt}#1%
}
\begin{document}
AAA\fbox{\parbox{4em}{aa bb cc dd ee ff}}AAA
BBB\fbox{\myparbox{4em}{aa bb cc dd ee ff}}BBB
BBB\fbox{\myparbox[t][12ex][s]{4em}{aa bb cc dd ee ff\par\vfil aa bb}}BBB
\end{document}