TeX - LaTeX Asked by mathspasha on December 15, 2020
I am trying to create a command that the user can enter keys for values. How can I create one, for example:
myparbox[width=50,height=10,color=blue, align=left -10px]{}
Use pgfkeys
! There are three steps to this: first, you must make your command accept keys as options. Traditionally, this is as you wrote it: an optional argument. In that case, you should start your definition like this:
newcommandmyparbox[2][]{%
pgfkeys{#1}%
...
}
The syntax is that the optional argument is expected to contain a list of keys (possibly set to values) that are then passed to pgfkeys
for processing. The second step is to figure out what you're going to do with the results: that is, you imagine that pgfkeys
does some kind of magic and produces a bunch of macros, or conditionals, and you need to make these things affect the operation of myparbox
.
Let's take the easy ones as an example: width
and height
. You will probably just pass them to parbox
as the optional parameters that control the width and height, and a good way to do that is to store their values in a macro. Let's say that width
goes to the macro myparboxWidth
and height
goes to myparboxHeight
. Then your definition of myparbox
will look more like:
newcommandmyparbox[2][]{%
pgfkeys{#1}%
parbox[t][myparboxHeight]{myparboxWidth}{#2}%
}
I had to write [t]
for the first optional argument in parbox
, which specifies the position in the surrounding text, because height is the second argument. This suggests that we ought to have a position
key as well that corresponds to a macro myparboxPosition
. There's a third optional argument that I didn't give, but it's the "inner position", which can be either top, bottom, centered, or stretched. Might as well have an inner position
key that sets myparboxInnerPos
. That gives:
newcommandmyparbox[2][1]{%
pgfkeys{#1}%
parbox[myparboxPosition][myparboxHeight]
[myparboxInnerPos]{myparboxWidth}{#2}
}
That's enough for now. In order to make this work, you have to define your keys, and that's where pgfkeys
is far, far better than its competitors. You can tell it to do all sorts of things with the values other than just storing them, though for height
and width
that will be enough. You define keys by using pgfkeys
in the preamble to set things up:
usepackage{pgfkeys}
pgfkeys{
/myparbox/.is family, /myparbox,
width/.estore in = myparboxWidth,
height/.estore in = myparboxHeight,
}
This has a few features. The real action is that I've said that both of these keys will "pass through" their arguments to the respective macros; if you said "width = 10pt" in the options to myparbox
, then myparboxWidth
would get set to 10pt
. I wrote .estore in
rather than plain .store in
to force the value to be expanded before being saved; this prevents subtle errors if someone passes a macro that could get changed somehow before being used in myparbox
.
The other feature is that I've put the keys in a family, called /myparbox
. In pgfkeys
jargon, this is a "directory", like in a file system. Calling the /myparbox
key changes directory to this one, and then all keys are private to that directory. This prevents name clashes with the (very common) key names width
and height
. Now you have to modify your pgfkeys
call in myparbox
as well to change directory:
newcommandmyparbox[2][1]{%
pgfkeys{/myparbox, #1}%
...
}
For the position arguments, it would be nice if they could have more...logical names than simply "t", "b", "c", or "s". Since pgfkeys
is, at heart, a lookup engine, it is pretty easy to have it map logical names to various actions: you just make each name a key that points to the corresponding action. I would do the following:
pgfkeys{
/myparbox,
position/.style = {positions/#1/.get = myparboxPosition},
inner position/.style = {positions/#1/.get = myparboxInnerPos},
positions/.cd,
top/.initial = t,
center/.initial = c,
bottom/.initial = b,
stretch/.initial = s,
}
This is much more intricate than width
and height
, so I'll take it apart.
First, we have the basic position
and inner position
keys, which are passed values. pgfkeys
treats these keys like macros with one argument, so the value is available as #1
. We tell them to store the values in the appropriate place. The /.style
suffix is a "handler" that defines a more complex behavior for a key than just setting a value; in this case, it makes the key "expand" to other keys that are then called to continue the work.
What gets stored, though, has to be properly formatted: parbox
expects those one-character options and not words. So we define a positions
subdirectory containing all the words we want to accept, defined to contain their translations into parbox
-speak. (As before, we isolate these special keys in a directory where they can't be seen and won't conflict with real options.) The /.initial
handler sets values for keys the first time they are seen (these keys will never be redefined, actually).
Back in position
and inner position
, the way we actually store the values is by using the /.get
handler for the appropriate positions/
subkey. What this does is simply copy the value in that key into the named macro, which is what we wanted: position = top
becomes defmyparboxPosition{t}
(effectively).
There is one more complication to take care of: what happens if you only specify half the options? The remaining macros myparboxWhatever
will be undefined or, more insidiously, defined to be whatever they got set to the last time they called myparbox
. We need to establish some defaults. The easiest way of doing that is to make a default
style key that we run before processing the options in myparbox
. It may look like this:
pgfkeys{
/myparbox,
default/.style =
{width = textwidth, height = baselineskip,
position = center, inner position = center}
}
Then the pgfkeys
call in myparbox
becomes
pgfkeys{/myparbox, default, #1}
Here is the final result:
documentclass{article}
usepackage{pgfkeys}
% Set up the keys. Only the ones directly under /myparbox
% can be accepted as options to the myparbox macro.
pgfkeys{
/myparbox/.is family, /myparbox,
% Here are the options that a user can pass
default/.style =
{width = textwidth, height = baselineskip,
position = center, inner position = center},
width/.estore in = myparboxWidth,
height/.estore in = myparboxHeight,
position/.style = {positions/#1/.get = myparboxPosition},
inner position/.style = {positions/#1/.get = myparboxInnerPos},
% Here is the dictionary for positions.
positions/.cd,
top/.initial = t,
center/.initial = c,
bottom/.initial = b,
stretch/.initial = s,
}
% We process the options first, then pass them to `parbox` in the form of macros.
newcommandmyparbox[2][]{%
pgfkeys{/myparbox, default, #1}%
parbox[myparboxPosition][myparboxHeight]
[myparboxInnerPos]{myparboxWidth}{#2}
}
begin{document}
% This should print "Some text, and"
% followed by "a box" raised about one line above the natural position
% followed by "and more text" after a large space.
Some text, and myparbox[width = 50pt, height = 20pt, position = bottom, inner position = top]{a box} and more text.
% Should look pretty much like normal text, with slight offsets down and over around the box.
Some text, and myparbox[width = 30pt]{a box} and more text.
% The box should have very spread-out lines
Some text, and
myparbox[width = 30pt, height = 100pt, inner position = stretch]
{a boxpar vspace{stretch{1}}withparvspace{stretch{1}}words}
and more text.
end{document}
Using these techniques, you can (perhaps not easily at first) craft your own options and make them tweak the behavior of myparbox
. For example, if you wanted to have a color
option, you would link it to the argument of a textcolor
command.
Correct answer by Ryan Reich on December 15, 2020
Here is a short example of how to use keys by virtue of the keyval
package.
The steps you need to follow are all based on the following macro:
define@key{<family>}{<key>}{<function>}
Define your <family>
: Use the above command by choosing some <family>
name that all the keys will be associated with. In my example below, I chose the family name myparbox
, since the keys should be associated with the macro myparbox
. They need not be the same as in my case.
Define your <key>
s: You list all the keys that should be allowed for the <family>
. In my example I defined the keys fontcolor
, color
, width
and align
, since these will have a meaning within my myparbox
command.
Define a <function>
for each <key>
: Whenever someone uses the <key>=<value>
, <function>
takes <value>
as its argument #1
. So, in the example below, I assign a macro for each one of the assigned values. This allows me to capture <value>
so it can be used later. Note how the macro for each <key>
is prefixed with pb@
(short for p
arb
ox@
). This is because you may have a whole bunch of keys that you define, and you don't want the macros to clash with other packages, so the prefix makes each macro even more unique and further avoid clashes.
As a more elaborate discussion on functions, consider the color
key. The macro associated with the color
key is defined as defpb@color{#1}
. That is, whenever someone uses color=<some color>
, it executes defpb@color{<some color>}
, thereby assigning the color <some color>
to the macro pb@color
.
Set your default values: This is done by using the setkeys{<family>}{<key>=<value> list}
command. In the example below, I listed the default key-value pairs as
fontcolor=black
color=white
width=5cm
align=t
Write your macro that uses the created <key>
s: You macro has the following basic form:
newcommand{<mymacro>}[2][]{%
setkeys{<family>}{#1}% Set the keys
% do something with #2
}
This defines <mymacro>
with 2 arguments, the first of which is optional. That is, <mymacro>[<key>=<value> list]{<stuff>}
. At the first step, I assign whatever is passed as the optional argument #1
to setkeys
under the same family name as my key set (myparbox
in the example below). Secondly, I typeset #2
using the macros defined by the keys.
documentclass{article}
usepackage{xcolor}% http://ctan.org/pkg/xcolor
usepackage{keyval}% http://ctan.org/pkg/keyval
makeatletter
% ========= KEY DEFINITIONS =========
newlength{pb@width}
define@key{myparbox}{fontcolor}{defpb@fontcolor{#1}}
define@key{myparbox}{color}{defpb@color{#1}}
define@key{myparbox}{width}{setlengthpb@width{#1}}
define@key{myparbox}{align}{defpb@align{#1}}
% ========= KEY DEFAULTS =========
setkeys{myparbox}{fontcolor=black,color=white,width=5cm,align=t}%
newcommand{myparbox}[2][]{%
begingroup%
setkeys{myparbox}{#1}% Set new keys
colorbox{pb@color}{parbox[pb@align]{pb@width}{%
color{pb@fontcolor}#2
}}%
endgroup%
}
makeatother
begin{document}
myparbox{%
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
}
myparbox[width=10cm]{%
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
}
myparbox[width=7cm,fontcolor=red,color=blue]{%
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
Here is some text that should fit in this paragraph box.
}
end{document}
The xkeyval
package provides a similar, yet more advanced interface.
Answered by Werner on December 15, 2020
As noted at here, setkeys
from the keyval package may not be nested. This is because it doesn't push any current state before commencing process. Besides this, we no longer have to repeatedly call define@key
to define several keys. Here is a key command approach.
documentclass{article}
usepackage{xcolor}
usepackage{ltxkeys}
makeatletter
% To avoid local groups when using fbox parameters, the development version of
% ltxkeys package introduces the commands ltxkeys@savefboxparam and
% ltxkeys@restorefboxparam.
% The commands ltxkeys@initializekeys and ltxkeys@launchkeys can be used to
% re-initialize keys to their default values. This avoids creating local
% groups when setting keys, but (by design) these commands will not re-initialize
% 'option keys' (ie, keys that are package or class options). The ltxkeys
% package deals with this via the hooks ltxkeys@beforekeycmdsetkeys,
% ltxkeys@beforekeycmdbody, ltxkeys@afterkeycmdbody, and the commands
% ltxkeys@savecmdkeyvalues and ltxkeys@restorecmdkeyvalues, all of which apply
% to only key commands.
%
new@def*ltxkeys@fboxparamstack{}
robust@def*ltxkeys@savefboxparam{%
xdefltxkeys@fboxparamstack{%
fboxrule=thefboxrulerelaxfboxsep=thefboxseprelax
noexpand@nil{expandcsonceltxkeys@fboxparamstack}%
}%
}
robust@def*ltxkeys@restorefboxparam{%
begingroup
defx##1@nil{endgroup##1gdefltxkeys@fboxparamstack}%
expandafterxltxkeys@fboxparamstack
}
% myparbox is defined as a robust command:
ltxkeysrobustltxkeyscmdmyparbox[2][](%
cmd/textcolor/black;
cmd/framecolor/white;
cmd/fillcolor/white;
cmd/framerule/.4pt;
cmd/framesep/3pt;
cmd/width/5cm;
cmd/align/t;
bool/testbool/true;
){%
ltxkeys@savefboxparam
letkvalkeyval
fboxrule=kval{framerule}relax
fboxsep=kval{framesep}relax
fcolorbox{kval{framecolor}}{kval{fillcolor}}{%
parbox[kval{align}]{kval{width}}{%
color{kval{textcolor}}%
#2ifkeyvalTF{testbool}{texttt{textcolor{black}{<<#1>>}}}{}%
}%
}%
ltxkeys@restorefboxparam
}
makeatother
begin{document}
newcommand*sometext[1][1]{%
cptdotimes{#1}{%
Here is some text that should fit in this paragraph box.
}%
}
% No keys called here:
myparbox{sometext[3]}
parmedskip
% Keys come last, if they are called:
myparbox{sometext[3]}
(width=10cm,framecolor=green,framerule=1pt,fillcolor=gray!15)
parmedskip
myparbox[Optional text]{sometext[4]}
(width=7cm,framerule=4pt,framesep=20pt,textcolor=red,framecolor=brown,
fillcolor=yellow!25,testbool)
end{document}
Answered by Ahmed Musa on December 15, 2020
The question is not explicitly about package writing, therefore I wonder, why until now noone mentioned keycommand
, what was made to provide for document writers “an easy way to define commands or environments with optional keys” (cite from package manual), but this package could used inside of packages, as well. So, let me show this here.
Note, that there is a bug in this package, hence one has to add the patch provided by Joseph Wright in his answer to the question How do I use ifcommandkey , or how do I check if a key was given? (as can also be supposed from the title of this question, the patch is strictly speaking only necessary, if one wants to utilise the ifcommandkey
command, but this will very often be the case):
usepackage{keycommand}
% patch by Joseph Wright ("bug in the definition of ifcommandkey (2010/04/27 v3.1415)"),
% https://tex.stackexchange.com/a/35794/
begingroup
makeatletter
catcode`/=8 %
@firstofone
{
endgroup
renewcommand{ifcommandkey}[1]{%
csname @expandafter expandafter expandafter
expandafter expandafter expandafter expandafter
kcmd@nbk commandkey {#1}//{first}{second}//oftwoendcsname
}
}
%=======================%
Now with this package and using the simplified approach of Ryan Reich (giving the whole optional arguments) the definition of myparbox
will be
newkeycommand{myparbox}
[enum vertalign={c,t,b}, boxheight=height, enum innervertalign={c,t,b}, width][1]
{%
parbox[commandkey{vertalign}][commandkey{boxheight}]
[commandkey{innervertalign}]{commandkey{width}}{#1}%
}
Here enum …
is one of the two different choice types for key definition. (BTW: in my humble opinion the package author mixed up the meaning of both, because with the enum
type one has later not to give a number in the key, but with the choice
key.)
Note, that despite width
is given as optional argument like all other keys, it is mandatory, because the underlying parbox
has a mandatory width
argument. If the other three keys later are left out, the value given in definition is used, for keys from both choice
types this is the first of the value list.
Adding definitions for colour keys and the conditional ifcommandkey{<key>}{<key> value not blank}{<key> value blank}
makes it more complex and more complicated:
newkeycommand{myparbox}
[enum vertalign={c,t,b}, boxheight=height, enum innervertalign={c,t,b},
width, backgroundcolor, textcolor][1]
{%
ifcommandkey{backgroundcolor}{colorbox{commandkey{backgroundcolor}}
{parbox[commandkey{vertalign}][commandkey{boxheight}]
[commandkey{innervertalign}]{commandkey{width}}
{ifcommandkey{textcolor}{color{commandkey{textcolor}}}{}#1}%
}}
{parbox[commandkey{vertalign}][commandkey{boxheight}]
[commandkey{innervertalign}]{commandkey{width}}
{ifcommandkey{textcolor}{color{commandkey{textcolor}}}{}#1}%
}%
}
The hardest part is to get the brace pairs right and not to forget a brace.
All together:
documentclass{article}
usepackage[T1]{fontenc}
usepackage{lmodern}
usepackage[svgnames]{xcolor}
usepackage{keycommand}
% patch by Joseph Wright ("bug in the definition of ifcommandkey (2010/04/27 v3.1415)"),
% https://tex.stackexchange.com/a/35794/
begingroup
makeatletter
catcode`/=8 %
@firstofone
{
endgroup
renewcommand{ifcommandkey}[1]{%
csname @expandafter expandafter expandafter
expandafter expandafter expandafter expandafter
kcmd@nbk commandkey {#1}//{first}{second}//oftwoendcsname
}
}
%=======================%
newkeycommand{myparbox}
[enum vertalign={c,t,b}, boxheight=height, enum innervertalign={c,t,b},
width, backgroundcolor, textcolor][1]
{%
ifcommandkey{backgroundcolor}{colorbox{commandkey{backgroundcolor}}
{parbox[commandkey{vertalign}][commandkey{boxheight}]
[commandkey{innervertalign}]{commandkey{width}}
{ifcommandkey{textcolor}{color{commandkey{textcolor}}}{}#1}%
}}
{parbox[commandkey{vertalign}][commandkey{boxheight}]
[commandkey{innervertalign}]{commandkey{width}}
{ifcommandkey{textcolor}{color{commandkey{textcolor}}}{}#1}%
}%
}
begin{document}
Xmyparbox[width=0.65em]{Z Z}X --
Xmyparbox[width=0.65em, backgroundcolor=SkyBlue]{Z Z}X --
Xmyparbox[vertalign=b, width=0.65em, backgroundcolor=SkyBlue]{Z Z}X --
Xmyparbox[vertalign=b, boxheight=3baselineskip, width=0.65em,
backgroundcolor=SkyBlue]{Z Z}X --
Xmyparbox[vertalign=b, innervertalign=b, boxheight=3baselineskip,
width=0.65em, backgroundcolor=SkyBlue]{Z Z}X --
Xmyparbox[vertalign=t, innervertalign=b, boxheight=3baselineskip,
width=0.65em, backgroundcolor=SkyBlue,textcolor=Gold]{Z Z}X --
Xmyparbox[vertalign=t, innervertalign=t, boxheight=3baselineskip,
width=0.65em, backgroundcolor=SkyBlue,textcolor=Gold]{Z Z}X
end{document}
A feature not possible with keyval
, but with newer, more advanced packages (e.g. pgfkeys
), is the handling of not explicitly defined (“unknown”) keys. This is useful, when the command inside of new command definition itself already works with a key-value-approach. keycommand
provides for these cases another optional argument, where an arbitrary key name has to be given (most useful something along “OtherKeys”/“OrigKeys”). Then all keys not known to the new key command are simply handed over to the underlying command.
See the following example, I needed to use the version, where an expansion delay is provided for commands in |
pairs (defined in optional argument before new command name):
documentclass{article}
usepackage[T1]{fontenc}
usepackage{lmodern}
usepackage{graphicx,transparent}
usepackage{keycommand}
% patch by Joseph Wright ("bug in the definition of ifcommandkey (2010/04/27 v3.1415)"),
% https://tex.stackexchange.com/a/35794/
begingroup
makeatletter
catcode`/=8 %
@firstofone
{
endgroup
renewcommand{ifcommandkey}[1]{%
csname @expandafter expandafter expandafter
expandafter expandafter expandafter expandafter
kcmd@nbk commandkey {#1}//{first}{second}//oftwoendcsname
}
}
%=======================%
newkeycommand+[|]{transparentimage}[opacity][origkeys][1]
{%
begingroup
ifcommandkey{opacity}{|transparent|{commandkey{opacity}}}{}
|includegraphics|[commandkey{origkeys}]{#1}
endgroup%
}
begin{document}
centering
transparentimage{example-grid-100x100pt.pdf}
transparentimage[opacity=0.33]{example-grid-100x100pt.pdf}
transparentimage[width=75pt]{example-grid-100x100pt.pdf}
transparentimage[width=75pt,opacity=0.33]{example-grid-100x100pt.pdf}
transparentimage[angle=45,width=106pt]{example-grid-100x100pt.pdf}
transparentimage[angle=45,width=106pt,opacity=0.33]{example-grid-100x100pt.pdf}
end{document}
Here the only new defined key is opacity
, the keys width
and angle
are original keys from includegraphics
.
Answered by Speravir on December 15, 2020
Nowadays there is another package worth mentioning: l3keys
(which is part of expl3
). It is especially handy when using expl3 to define the low-level commands, but it is also handy otherwise. To define the command itself use xparse
.
Code example (prototype, optional arguments of parbox
not functionally implemented):
documentclass{article}
usepackage{xparse}
usepackage{xcolor}
ExplSyntaxOn
keys_define:nn { keyval }
{
width .dim_set:N = l__keyval_width_dim,
height .dim_set:N = l__keyval_height_dim,
height .initial:n = { -1cm }, % dummy value for absence check
color .tl_set:N = l__keyval_color_tl,
color .initial:n = black
}
cs_new:Npn keyval_parbox:n #1
{
parbox
[][dim_use:N l__keyval_height_dim]
{ dim_use:N l__keyval_width_dim }
{ color { l__keyval_color_tl } #1 }
}
NewDocumentCommand { myparbox } { O{} +m }
{
group_begin:
keys_set:nn { keyval } { #1 }
keyval_parbox:n { #2 }
group_end:
}
ExplSyntaxOff
begin{document}
myparbox[width=50mm,height=15pt,color=blue]{Quack}
myparbox[width=50mm,height=2pt,color=red]{Quack}
myparbox[width=50mm,color=yellow]{Quack}
end{document}
Answered by TeXnician on December 15, 2020
expkv
There are different ways on how to achieve this with expkv
and related packages which I present in the following.
expkv
The main package of expkv
does only provide a very low level interface comparable to the widely known keyval
package. You can define new keys with ekvdef
and there are no predefined handlers doing your job.
For instance to let a key store something in a macro you'd do
ekvdef{set}{key}{defmacro{#1}}
and to define an action for a key without a value you'd use
ekvdefNoVal{set}{key}{action}
The following uses these two macros to define keys to get a mypbox
command.
documentclass[]{article}
usepackage{expkv}
usepackage[]{color}
makeatletter
letifmypbox@frameiffalse
protecteddefmypbox@frametrue
{letifmypbox@frameiftrueletmypbox@framefbox}
protecteddefmypbox@framefalse
{letifmypbox@frameiffalseletmypbox@frame@firstofone}
protectedekvdef{mypbox}{width}{defmypbox@wd{#1}}
protectedekvdef{mypbox}{height}{defmypbox@ht{#1}}
protectedekvdef{mypbox}{align}{defmypbox@pos{#1}}
protectedekvdef{mypbox}{content}{defmypbox@cpos{#1}}
protectedekvdef{mypbox}{color}{defmypbox@color{#1}}
protectedekvdef{mypbox}{frame sep}{fboxsep=#1relax}
protectedekvdefNoVal{mypbox}{c}{defmypbox@pos{c}}
protectedekvdefNoVal{mypbox}{t}{defmypbox@pos{t}}
protectedekvdefNoVal{mypbox}{b}{defmypbox@pos{b}}
protectedekvdefNoVal{mypbox}{cc}{defmypbox@cpos{c}}
protectedekvdefNoVal{mypbox}{ct}{defmypbox@cpos{t}}
protectedekvdefNoVal{mypbox}{cb}{defmypbox@cpos{b}}
protectedekvdef{mypbox}{frame}{mypbox@frametruedefmypbox@framecolor{#1}}
protectedekvdefNoVal{mypbox}{frame}
{mypbox@frametrueletmypbox@framecolor@empty}
protectedekvdefNoVal{mypbox}{no frame}
{mypbox@framefalseletmypbox@framecolor@empty}
newcommand*mypbox@wd{linewidth}
newcommand*mypbox@color{black}
newcommand*mypbox@ht{}
newcommand*mypbox@cpos{t}
newcommand*mypbox@pos{t}
letmypbox@frame@firstofone
ekvsetdefmypbox@set{mypbox}
newcommand*mypbox[2][]
{%
begingroup
mypbox@set{#1}%
ifmypbox@frame
unlessifxmypbox@framecolor@empty
expandaftercolorexpandafter{mypbox@framecolor}%
fi
fi
mypbox@frame
{%
ifxmypbox@ht@empty
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
{parbox[mypbox@pos]}%
{parbox[mypbox@pos][mypbox@ht][mypbox@cpos]}%
{mypbox@wd}
{%
unlessifxmypbox@color@empty
expandaftercolorexpandafter{mypbox@color}%
fi
#2%
}%
}%
endgroup
}
makeatother
begin{document}
noindent
mypbox{This is some text}
noindent
mypbox[frame=blue,frame sep=-fboxrule]{This is some text}
noindent
mypbox[frame=red,width=5cm,height=3cm,cc,c]{This is some text}
mypbox[frame,height=1cm,b,width=3cm]{This is some text}
mypbox[frame=green,color=magenta,height=5mm,t,width=3cm]{This is some text}
end{document}
expkv-def
The expkv-def
package adds common key types and an interface to define such keys in a similar way to pgfkeys
or l3keys
. The following example defines the equivalent keys to the example above, but using the expkv-def
syntax.
documentclass[]{article}
usepackage{expkv-def}
usepackage[]{color}
makeatletter
ekvdefinekeys{mypbox}
{
store width = mypbox@wd
,initial width = linewidth
,store height = mypbox@ht
,store align = mypbox@pos
,initial align = t
,store content = mypbox@cpos
,initial content = t
,store color = mypbox@color
,initial color = black
,dimen frame sep = fboxsep
,code frame = mypbox@frametruedefmypbox@framecolor{#1}
,noval frame = mypbox@frametrueletmypbox@framecolor@empty
,noval no frame = mypbox@framefalseletmypbox@framecolor@empty
,nmeta c = align = c
,nmeta t = align = t
,nmeta b = align = b
,nmeta cc = content = c
,nmeta ct = content = t
,nmeta cb = content = b
}
letifmypbox@frameiffalse
protecteddefmypbox@frametrue
{letifmypbox@frameiftrueletmypbox@framefbox}
protecteddefmypbox@framefalse
{letifmypbox@frameiffalseletmypbox@frame@firstofone}
letmypbox@frame@firstofone
ekvsetdefmypbox@set{mypbox}
newcommand*mypbox[2][]
{%
begingroup
mypbox@set{#1}%
ifmypbox@frame
unlessifxmypbox@framecolor@empty
expandaftercolorexpandafter{mypbox@framecolor}%
fi
fi
mypbox@frame
{%
ifxmypbox@ht@empty
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
{parbox[mypbox@pos]}%
{parbox[mypbox@pos][mypbox@ht][mypbox@cpos]}%
{mypbox@wd}
{%
unlessifxmypbox@color@empty
expandaftercolorexpandafter{mypbox@color}%
fi
#2%
}%
}%
endgroup
}
makeatother
begin{document}
noindent
mypbox{This is some text}
noindent
mypbox[frame=blue,frame sep=-fboxrule]{This is some text}
noindent
mypbox[frame=red,width=5cm,height=3cm,cc,c]{This is some text}
mypbox[frame,height=1cm,b,width=3cm]{This is some text}
mypbox[frame=green,color=magenta,height=5mm,t,width=3cm]{This is some text}
end{document}
expkv-cs
The expkv-cs
package provides an interface to define a macro that takes a list of key=value pairs and handles it as separate arguments. This mechanism is fully expandable (but our example is not). The arguments will be passed on in the order the keys were specified.
Additionally there is a second mechanism, which forwards the key=value pairs as a formatted list from which specific keys can be called with helper macros. This variant will not be shown in this answer.
documentclass[]{article}
usepackage{expkv-cs}
usepackage[]{color}
makeatletter
ekvcSplitAndForwardmypbox@splitmypbox@do
{
frame = relax
,align = t
,height = relax
,content = t
,width = linewidth
,color = relax
,frame sep = fboxsep
}
ekvcSecondaryKeysmypbox@split
{
default frame = @empty
,nmeta no frame = frame = relax
,nmeta c = align = c
,nmeta t = align = t
,nmeta b = align = b
,nmeta cc = content = c
,nmeta ct = content = t
,nmeta cb = content = b
}
newcommandmypbox@ifx[2]
{%
ifx#1#2expandafter@firstoftwoelseexpandafter@secondoftwofi
}
newcommandmypbox[1][]
{%
mypbox@split{#1}
}
newcommandmypbox@do[8]
{%
% #1: frame
% #2: align
% #3: height
% #4: content
% #5: width
% #6: color
% #7: frame sep
% #8: text
begingroup
mypbox@ifxrelax{#1}%
{@firstofone}
{%
mypbox@ifx@empty{#1}%
{}
{color{#1}}%
fboxsep=#7relax
fbox
}%
{%
mypbox@ifxrelax{#3}%
{parbox[#2]}
{parbox[#2][#3][#4]}%
{#5}
{%
mypbox@ifxrelax{#6}%
{}%
{color{#6}}%
#8%
}%
}%
endgroup
}
makeatother
begin{document}
noindent
mypbox{This is some text}
noindent
mypbox[frame=blue,frame sep=-fboxrule]{This is some text}
noindent
mypbox[frame=red,width=5cm,height=3cm,cc,c]{This is some text}
mypbox[frame,height=1cm,b,width=3cm]{This is some text}
mypbox[frame=green,color=magenta,height=5mm,t,width=3cm]{This is some text}
end{document}
The results of all three examples look the same:
Answered by Skillmon on December 15, 2020
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP