6

I learnt that \parbox has five arguments, that is: \parbox[<align>][<height>][<inner-align>]{<width>}{<text>}. So I constrcut my own \myparbox in which I add \sloppy\setlength\parfillskip{0pt} before #5. But it fails. What's the reason and how to re-define \myparbox?

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}

EDIT:

\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}
5
  • 1
    An empty optional argument [] is not always the same as not giving an optional argument at all. Compare \parbox[][]{4em}{aa bb cc dd ee ff} and \parbox[]{4em}{aa bb cc dd ee ff}. In the xparse example things get worse because o's #1 becomes the special value -noValue- when no optional argument is given. Naturally \parbox can't deal with that.
    – moewe
    Feb 21, 2019 at 13:38
  • I know that the default value of #1 is [c], #3 is [t], but what is the default value of #2 which can be used to define \myparbox?
    – lyl
    Feb 21, 2019 at 13:42
  • Instead of worrying about the defaults, it might just be better to run three test cases and run \parbox in three different ways. Then you do not need to know about the kernel defaults. Use say \IfNoValueTF{#1}{true}{false} and nest several of these.
    – daleif
    Feb 21, 2019 at 14:32
  • According to suggest above , I edited my MWE, but can not pass compile. why?
    – lyl
    Feb 21, 2019 at 14:39
  • as I said when you asked in comments under your previous question, there is no value that you can pass to the optional argument that is equivalent to not using the argument. Feb 21, 2019 at 15:15

3 Answers 3

8

As noted in the comments, an empty optional argument [] need not be equivalent to giving no optional argument at all. Whether not giving the argument is equivalent to passing a particular value must be checked with the documentation or implementation of the command.

In the example things get worse because xparse's optional o argument actually contain the special marked value -NoValue- if the corresponding optional argument was not given. You can and should test for presence of a value with \IfNoValueTF (as suggested by daleif's comment).

That is a very viable solution when you have only one optional argument to deal with, but it gets messy if the number of arguments increases.

In case of \parbox you can find out that the default arguments are c, \relax as special marker and s when you look up the definition in sourc2e.pdf.

So you could try

\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}

Normally I would try to work around issues like this that require me to know the default optional argument values/behaviour and or need a great number of \IfNoValueTF tests to get the arguments right as the first method seems fragile and the second very verbose and repetitive. An approach similar to Marijn's answer might be an alternative, but at least in this case also requires intimate knowledge of the definition of \parbox.


edit: I only just saw the new version of the question. \let is not enough for robust commands with optional arguments: When to use \LetLtxMacro?. I would also strongly suggest not to redefine fundamental commands such as \parbox even if the implementation might be backwards compatible. A new name is much safer.

7
  • I followed your answer and edited my MWE, but failed. Could you please have look at my edited MWE?
    – lyl
    Feb 21, 2019 at 14:45
  • 1
    @lyl That won't work because of the \let, see the edit bit of my answer. I could get something working with letltxmacro that compiles, but there is a weird side-effect. Try with \usepackage{letltxmacro}\LetLtxMacro{\oldparbox}{\parbox} instead of \let\oldparbox\parbox. It is a really bad idea to redefine such a fundamental command.
    – moewe
    Feb 21, 2019 at 14:47
  • @lyl Don't! Redefining the original \parbox will not end well...
    – moewe
    Feb 21, 2019 at 14:51
  • @lyl From where do you have information that \parbox supports \partbox*? That is probably why you cannot get \LetLtxMacro to work. As far as I can see from the sources \parbox does not support a star, so \oldparbox*{4em}{....} fails as it tries to use * as a length
    – daleif
    Feb 21, 2019 at 15:18
  • I just use [s] argument provided by xparse as a test to distinguish my parbox from the original \parbox. I think this is not a good idea as suggested in comments.
    – lyl
    Feb 22, 2019 at 2:18
3

Alternatively, you can use xpatch to insert the extra code at the right place in the command, without the need to pass variables around. The \parbox command calls the internal \@iiiparbox command which processes the actual contents, so this internal command is the one that should be patched. 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}

Note that this changes the behavior of all parboxes. If you want a custom command with the justified alignment and also keep the regular command, then you can define the custom command using a copy of the \@iiiparbox command and patch the copy instead of the original. 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}

Result:

enter image description here

1
  • Thank you for your solution with xpatch, I'll be learning this package.
    – lyl
    Feb 21, 2019 at 14:47
2

The optional arguments to \parbox must contain specific tokens, if present. The default value for the first is c, so we can spare some checks for the presence of the optional arguments. One needs to start from the last one.

The first optional argument should be c (default), t or b; the second one should be a length; the last one should be c, t, b or s (if not given, the first one is used).

Note that the last argument to \myparbox should by denoted by +m, in order to be able to input different paragraphs. The \deliverparbox macro is used in order to minimize code duplication.

\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}

enter image description here

3
  • +m is a much much valuable reminder which I have not think of!
    – lyl
    Feb 22, 2019 at 2:44
  • For \IfNoValueTF{#2}, the only diference between its two branches is whether #2 is given to \parbox. So, is there a way writing code like this: \parbox[#1] \if...{}{[#2]}{#4}{#5} .
    – lyl
    Feb 25, 2019 at 4:36
  • @lyl Depending on what the optional argument is expected to contain, there can be; not in this case.
    – egreg
    Feb 25, 2019 at 9:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .