Я узнал, что \parbox
у него пять аргументов, то есть: \parbox[<align>][<height>][<inner-align>]{<width>}{<text>}
. Поэтому я создаю свой собственный \myparbox, в который добавляю \sloppy\setlength\parfillskip{0pt}
перед #5. Но он терпит неудачу. В чем причина и как переопределить \myparbox?
МВЭ:
\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}
РЕДАКТИРОВАТЬ:
\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}
решение1
Как отмечено в комментариях, пустой необязательный аргумент []
не обязательно эквивалентен отсутствию необязательного аргумента вообще. Эквивалентно ли отсутствие аргумента передаче определенного значения, необходимо проверить в документации или реализации команды.
В примере все становится еще хуже, потому что xparse
необязательный o
аргумент фактически содержит специальное отмеченное значение, -NoValue-
если соответствующий необязательный аргумент не был указан. Вы можете и должны проверить наличие значения с помощью \IfNoValueTF
(как предлагаетсядалейфкомментарий).
Это очень жизнеспособное решение, когда у вас есть только один необязательный аргумент, но оно становится запутанным, если количество аргументов увеличивается.
В случае, если \parbox
вы можете узнать, что аргументами по умолчанию являются c
, \relax
как специальный маркер, и s
когда вы смотрите определение в sourc2e.pdf
.
Так что вы можете попробовать
\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}
Обычно я пытаюсь обойти такие проблемы, которые требуют от меня знания значений/поведения необязательных аргументов по умолчанию и/или требуют большого количества \IfNoValueTF
тестов для правильного определения аргументов, поскольку первый метод кажется хрупким, а второй — слишком многословным и повторяющимся. Подход, аналогичныйОтвет Марийнможет быть альтернативой, но, по крайней мере, в этом случае также требуется глубокое знание определения \parbox
.
редактировать: Я только что увидел новую версию вопроса. \let
недостаточно для надежных команд с необязательными аргументами:Когда использовать \LetLtxMacro?. Я бы также настоятельно рекомендовал не переопределять такие фундаментальные команды, как \parbox
даже если реализация может быть обратно совместима. Новое имя гораздо безопаснее.
решение2
В качестве альтернативы вы можете использовать xpatch
для вставки дополнительного кода в нужное место в команде, без необходимости передавать переменные. Команда \parbox
вызывает внутреннюю \@iiiparbox
команду, которая обрабатывает фактическое содержимое, поэтому эта внутренняя команда и должна быть исправлена. 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}
Обратите внимание, что это изменяет поведение всех парбоксов. Если вы хотите пользовательскую команду с выравниванием по ширине и при этом сохранить обычную команду, то вы можете определить пользовательскую команду, используя копию команды, \@iiiparbox
и исправить копию вместо оригинала. 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}
Результат:
решение3
Необязательные аргументы \parbox
должны содержать определенные токены, если они присутствуют. Значение по умолчанию для первого — c
, поэтому мы можем сэкономить некоторые проверки на наличие необязательных аргументов. Нужно начать с последнего.
Первый необязательный аргумент должен быть c
(по умолчанию) t
или b
; второй должен быть длиной; последний должен быть c
, t
, b
или s
(если не указан, используется первый).
Обратите внимание, что последний аргумент должен \myparbox
быть обозначен как +m
, чтобы можно было вводить разные параграфы. \deliverparbox
Макрос используется для минимизации дублирования кода.
\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}