2

I'm trying to make a new macro that takes either a scale size or a different scaling parameter (e.g. width=\textwidth) to size pictures. Here's what I have:

\newcommand{\pic}[3]{\begin{figure}[H]
    \centering
    \if\instring{#1}{=}\def \arg{#1}\else\def\arg{scale=#1}\fi
    \includegraphics[\arg]{#2}
    \caption{#3}
\end{figure}}

Later in my file, I have the following line:

\pic{1}{lpm_block.png}{Block diagram for the LPM multiplier}

which gives the error "package keyval error: scale=1 undefined". Without the 'if' statement, it works fine. What do I need to change? Is there a better way to do this? Thanks!

EDIT:

Here is is more code that should compile without issue.

\documentclass{article}

\usepackage{siunitx}
\usepackage{graphicx}
\usepackage{apacite}
\usepackage{amsmath} % Required for some math elements
\usepackage{multicol} % multiple columns
\usepackage{float} % place graphics
\usepackage{subfig} % graphics on same line
\usepackage{geometry} % fill empty margins
\usepackage[square,sort,comma,numbers]{natbib}
\usepackage[hyphens]{url}
\usepackage{minted}
\renewcommand\theFancyVerbLine{\normalsize\arabic{FancyVerbLine}}

\geometry{letterpaper, top=0.75in, left=0.75in, bottom=0.75in, right=0.75in}

\setlength\parindent{0pt} % Removes all indentation from paragraphs

%\usepackage{times} % Uncomment to use the Times New Roman font

\newcommand{\pic}[3]{\begin{figure}[H]
    %\if\instring{#1}{=}\def \arg{#1}\else\def\arg{scale=#1}\fi
    \centering
    \includegraphics[scale=#1]{#2}
    \caption{#3}
\end{figure}}

\begin{document}

\section{Design Files}
    \subsection{Serial Multiplier}
        \pic{0.55}{serial_block.png}{Block diagram for the serial multiplier. high9 and low8 are simply registers, so they do not have separate design files}

\end{document}

My goal in trying this with latex was to increase familiarity with making new commands using conditional statements. I didn't realize the nuances would make it a tad more difficult. Thanks for the input so far!

4
  • 2
    Welcome! Can you please make your example as-to-compile, so it gives us the error you report? What do you mean that it works without the if statement? Then \arg would be undefined, surely? The problem is that TeX is treating scale=1 as a single thing. You need to get that parsed out before passing it to \includegraphics. But defining a key-value interface yourself from scratch is quite a project. Is there some reason you don't want to use one of the existing frameworks for providing such interfaces? There is quite a choice by now - is really none suitable for your purpose?
    – cfr
    Commented Feb 23, 2018 at 2:58
  • It should compile now. If you can point me towards a better implementation, I'd be happy to try it out. As I mentioned in the edit, I want to become familiar with latex before being forced to in a dissertation :)
    – ntjess
    Commented Feb 24, 2018 at 3:11
  • If you're happy with the optional argument approach, you can do it that way. However, I would caution against implementing this. key-value syntax is increasingly popular for good reason: it makes your code much more readable by others and by you 6 months or 6 years from now. scale=0.5 speaks more-or-less for itself. \pic[0.5]... might be almost anything. Also, best to avoid \pic if you think you ever might want to use TikZ. (I keep seeing your code as concerned with TikZ pics.)
    – cfr
    Commented Feb 24, 2018 at 4:02
  • I appreciate your input, it appears putting time into learning TikZ would be beneficial. Thank you for your time!
    – ntjess
    Commented Feb 24, 2018 at 5:11

4 Answers 4

0

I take this as an exercise, as you say. Comments later.

What you want is deciding whether = appears in the first argument.

\documentclass{article}
\usepackage{graphicx}

\newcommand{\pic}[3]{%
  \begin{figure}
  \centering
  \checkequals{#1}%
  \expandafter\includegraphics\expandafter[\picarg]{#2}
  \caption{#3}
  \end{figure}%
}
\makeatletter
\newcommand\checkequals[1]{\check@equals{#1}#1=\@nil}
\def\check@equals#1#2=#3\@nil{%
  \if\relax\detokenize{#3}\relax
    % no =
    \def\picarg{scale=#2}%
  \else
    \def\picarg{#1}%
  \fi
}
\makeatother

\begin{document}

\pic{0.55}{example-image}{First example}

\pic{scale=0.55}{example-image}{Second example}

\pic{width=2cm,height=1cm}{example-image}{Third example}

\end{document}

The \checkequals macro calls the auxiliary \check@equals that takes delimited arguments; in case = doesn't appear, we define \picarg to be scale=#1, assuming the scale factor is specified. Otherwise, the original argument is stored in \picarg.

enter image description here

Now the problem is: do you really want such a macro? No, for several reasons.

  1. There's no great advantage in \pic{0.55}{x}{y} over \pic{scale=0.55}{x}{y}.

  2. scale is the wrong key to use, because you probably don't know the size of the picture, nor the final size of the document. A small change in the text width will force you to rescale several pictures.

  3. There's no advantage in hiding figure in \pic; if you have two pictures that can fit a line, you might want to set them side by side.

  4. The [H] option is a perfect choice for spoiling your document. If a figure has to stay there and nowhere else, then it needs no caption, because its description will be next to it anyhow. Using floats help in better organizing the material in order to have good pagination as a bonus.

Final remark. Your code

%\usepackage{times} % Uncomment to use the Times New Roman font

is saying a lie. The times package would only change the text fonts, not the math fonts. If you really want to use Times, do

\usepackage{newtxtext}
\usepackage{newtxmath}% with possible option, see the manual for newtx
1
  • Thanks for the input. I am self-taught, so I appreciate any pointers for improvement. Are there any good tutorials or latex walkthroughs you can recommend so I improve my practice? So far, I've just been looking at various templates and other Exchange questions to write my documents.
    – ntjess
    Commented Feb 24, 2018 at 19:57
1

Package keyval uses the comma , as separator of key-value pairs and the equal sign = to separate the key from the value. It does not expand macros. Therefore, these syntax characters must not be hidden in macros.

The example in the question is fixed by expanding \arg via \expandafter before \includegraphics processes its optional argument:

\if\instring{#1}{=}\def \arg{#1}\else\def\arg{scale=#1}\fi
\expandafter\includegraphics\expandafter[\arg]{#2}%
1
  • Thanks for the suggestion! I tried this, and ended up with what follows: Code: \newcommand{\pic}[3]{\begin{figure}[H] \if\instring{#1}{=}\def \arg{#1}\else\def\arg{scale=#1}\fi \centering \expandafter\includegraphics\expandafter[\arg]{#2}% \caption{#3} \end{figure}} Result: "Undefined control sequence... The control sequence at the end of the top line of your error message was never \def'ed...
    – ntjess
    Commented Feb 24, 2018 at 3:14
0

If you use an optional argument that defaults to scale=1 then you do not need to check to see whether or not it is empty:

\documentclass{article}
\usepackage{mwe}

\newcommand{\pic}[3][scale=1]{%
  \begin{figure}
    \centering
    \includegraphics[#1]{#2}
    \caption{#3}
  \end{figure}%
}

\begin{document}

   \pic{example-image-1x1}{Example image}

   \pic[width=\textwidth]{example-image-1x1}{Wider image}
\end{document}

This produces the following two pages:

enter image description here

3
  • [scale=1] could (more efficiently) be implemented as [] as that would avoid scaling at all, but the OP wants to be able to use 0.5 to mean scale=0.5 (or equivalently [width=.5\width]) Commented Feb 23, 2018 at 9:07
  • Exactly. If there's an easier / more conventional way, I'd appreciate some pointers to resources. I had difficulty finding them online...
    – ntjess
    Commented Feb 24, 2018 at 3:17
  • @ntjess But you can make 1 the default. This will be less efficient in the default case, but will allow you to give just .5 as the first optional argument, if you want.
    – cfr
    Commented Feb 24, 2018 at 3:58
0

Note that I don't think this is wise at all and I recommend rethinking your desiderata. However, if it I was intent on wanton destruction, I would use LaTeX 3 to wreak it. But that is probably not the best place to start if you're just learning LaTeX 2e. Hence, if you must do it, then one way you might try to do it is as follows.

\documentclass{article}
\usepackage{graphicx,caption}
\makeatletter
\def\pict@aux#1=#2\@nil{#2}
\newcommand{\pict}[3]{%
  \begin{figure}
    \centering
    \edef\tempa{\expandafter\pict@aux#1=\@nil}%
    \edef\tempb{}%
    \ifx\tempa\tempb
    \includegraphics[scale=#1]{#2}%
    \else
    \includegraphics[#1]{#2}%
    \fi
    \captionof{figure}{#3}%
  \end{figure}%
}
\makeatother

\begin{document}

\section{Design Files}
\subsection{Serial Multiplier}
\pict{0.55}{\jobname-cx}{Block diagram for the serial multiplier. high9 and low8 are simply registers, so they do not have separate design files}
\pict{width=0.55cm}{\jobname-cx}{Block diagram for the serial multiplier. high9 and low8 are simply registers, so they do not have separate design files}
\end{document}

variations

However, this is, in my opinion, a cack-handed design which spits on its users with contempt. Hence, I wouldn't do it - at least, not if I was going to be among the users. Your kilometres may, as always, vary.

You must log in to answer this question.

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