# Tikz image within a defined box (& the textpos package)

Short Description

I am trying to create a template for a multiple choice question that has an image associated with it. I want the question to look like this:

In this picture, I've shown the square that I want my Tikz image to appear in, and I've added some lines to show that the square should be lined up with the 5 (and always 5) answer choices.

I'm wondering if there's maybe some package like textpos that automatically takes the input, centers it in the box, and then resizes it to fill the whole box.

Long Description w/ MWE

(Bear with me here, I'm finding it hard to describe my problems)

I've set this up by using the textpos package to place the tikzpicture in its desired location. The issue here is that the tikzpicture is rendered, cropped (appropriately) to the dimensions of the tikzpicture, and then pushed to the top left (where the textpos "anchor" is located).

I want to be able to draw tikzpictures that are lots of different sizes and have different coordinates, but still have them be centered within the box shown in the above picture. For example, its easy to draw a circle by centering it at (0,0) and using all 4 quadrants, but for something like a right triangle, its easier to the 90deg angle at (0,0) and only use the first quadrant.

My attempts at this haven't been great, as I'm sort of fudging everything manually. I manually set the textpos anchor by moving it 0.6 of the paper width to the right and 3.415cm up from the 5th answer choice. After that, I've just drawn a handful of tikz pictures and made sure that their size exactly fits in the box (which happens to be 3.5cm x 3.5cm).

I've got some code below that might help explain my issues. I've included some dimension things (margins, parskip, etc.) as they are an important part of the project I'm working on. I've included some boundary lines to show that the image shouldn't be larger than the height of the 5 answer choices.

I think what I'm looking for is to just have the 3.5cm x 3.5cm box be its own thing that I can just place something in, and it will automatically center it and size it appropriately.

\documentclass[11pt, twoside]{article}

\usepackage[left=1in, top=1in, right=1in, bottom=1in, head=30pt, marginparwidth=2cm, marginparsep=0.35cm]{geometry}
\geometry{letterpaper}

\usepackage{parskip}

\usepackage{textpos}
\setlength{\TPHorizModule}{\the\paperwidth}
\setlength{\TPVertModule}{1cm}

\usepackage{tikz}

\newlength{\mylen}
\settoheight{\mylen}{C}

\begin{document}

\begin{enumerate}

\item Showing the box I want the image to appear in for the examples below.
\begin{enumerate}
\item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
\item Choice 2
\item Choice 3 \rule[0.5\mylen]{0.8\linewidth}{0.1pt}
\item Choice 4
\item Choice 5 \rule{0.65\linewidth}{0.1pt}
\end{enumerate}

\begin{textblock}{1}(0.6,-3.415)
\begin{tikzpicture}[scale = 3.5/200]
\draw[blue, fill = red!20,] (-100,-100) rectangle (100,100);
\end{tikzpicture}
\end{textblock}

\item Image is cropped to just include drawings, then pushed to top left of the red box.
\begin{enumerate}
\item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
\item Choice 2
\item Choice 3 \rule[0.5\mylen]{0.65\linewidth}{0.1pt}
\item Choice 4
\item Choice 5 \rule{0.65\linewidth}{0.1pt}
\end{enumerate}

\begin{textblock}{1}(0.6,-3.415)
\framebox{%
\begin{tikzpicture}[scale = 3.5/200]
\draw (0,0) node {Image};
\end{tikzpicture}
}
\end{textblock}

\item When the size or location of the image changes in tikz-land'', the tikzpicture is still pushed to the top left of the red box (as expected).
\begin{enumerate}
\item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
\item Choice 2
\item Choice 3 \rule[0.5\mylen]{0.65\linewidth}{0.1pt}
\item Choice 4
\item Choice 5 \rule{0.65\linewidth}{0.1pt}
\end{enumerate}

\begin{textblock}{1}(0.6,-3.415)
\framebox{
\begin{tikzpicture}[scale = 3.5/200]
\draw (10,10) node {\Huge{Image}};
\end{tikzpicture}
}
\end{textblock}

\item How I want the image to look (without resizing it)
\begin{enumerate}
\item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
\item Choice 2
\item Choice 3 \rule[0.5\mylen]{0.65\linewidth}{0.1pt}
\item Choice 4
\item Choice 5 \rule{0.65\linewidth}{0.1pt}
\end{enumerate}

\begin{textblock}{1}(0.6,-3.415)
\begin{tikzpicture}[scale = 3.5/200]
\draw[blue] (-100,-100) rectangle (100,100);
\draw[] (0,0) node {Image};
\end{tikzpicture}
\end{textblock}

\item How I want the image to look (with resizing it)
\begin{enumerate}
\item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
\item Choice 2
\item Choice 3 \rule[0.5\mylen]{0.65\linewidth}{0.1pt}
\item Choice 4
\item Choice 5 \rule{0.65\linewidth}{0.1pt}
\end{enumerate}

\begin{textblock}{1}(0.6,-3.415)
\begin{tikzpicture}[scale = 3.5/200]
\draw[blue] (-100,-100) rectangle (100,100);
\draw[] (0,0) node {\Huge{Image}};
\end{tikzpicture}
\end{textblock}

\end{enumerate}

\end{document}


(Note: I've just manually created the box/image for numbers 4 and 5)

As can be seen here, changing the location of the node in "Tikz-land" doesn't affect its position (it still attaches to the top-left anchor point). If I change the size of the tikzpicture, it will still attach to the top-left, but just look bigger. I want it to attach to the center of the box and resize itself to fill the box (but no larger).

Sorry if I'm a little verbose, but I figured it would be better to try and fully explain myself now rather than have to come back and discuss everything again. Thanks in advance!

After implementing the solution provided by @frougon, I've noticed that when the Tikz-coordinates exceed \myheight and/or \mywidth (in this case, 3.5), the picture effectively "zoom's-out".

It would be great to have Tikz-pictures where the scaling/proportions don't matter. I would think this could be fixed by always scaling back the Tikz-picture so that the coordinates don't exceed 3.5 (see Image 4, where I scale the picture by 3.5/10, since 10 is the maximum coordinate value).

Maybe there is a definition such as \MaxCoordinate for all Tikz-pictures that I'm not aware of, but I'm not sure.

Here is my code and output:

\documentclass{article}
\usepackage{graphicx}
\usepackage{xcolor}
\usepackage{tikz}

% Frame dimensions
\newlength{\mywidth}
\newlength{\myheight}
\setlength{\mywidth}{3.5cm}
\setlength{\myheight}{3.5cm}

\makeatletter

\newenvironment{choices}[1]{%
\def\braced@stuff@to@include{{#1}}%
\par\noindent
\minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep}
\enumerate
\ignorespaces
}{%
\unskip
\endenumerate
\endminipage
\setlength{\fboxsep}{0pt}%
\fcolorbox{blue!20}{green!20}{%
\begin{minipage}[c][\myheight][c]{\mywidth}
\centering
\def\@tmp{%
\expandafter\@tmp\braced@stuff@to@include
\end{minipage}%
}%
\ignorespacesafterend
}

\makeatother

\begin{document}

\begin{enumerate}

\item Image 1
\begin{choices}{%
\begin{tikzpicture}
\draw (0,0) rectangle (1,1);
\draw (0.5,0.5) node {\textbullet};  % node placed in center of square
\end{tikzpicture}}
\item Choice 1
\item Choice 2
\item Choice 3
\item Choice 4
\item Choice 5
\end{choices}

\item Image 2: same proportions as Image 1, but it's larger
\begin{choices}{
\begin{tikzpicture}
\draw (0,0) rectangle (3.5,3.5);
\draw (1.75,1.75) node {\textbullet}; % node placed in center of square
\end{tikzpicture}
}
\item Choice 1
\item Choice 2
\item Choice 3
\item Choice 4
\item Choice 5
\end{choices}

\item Image 3: when the tikz-coordinates exceed 3.5, the image effectively zooms-out''
\begin{choices}{
\begin{tikzpicture}
\draw (0,0) rectangle (10,10);
\draw (5,5) node {\textbullet}; % node placed in center of square
\end{tikzpicture}
}
\item Choice 1
\item Choice 2
\item Choice 3
\item Choice 4
\item Choice 5
\end{choices}

\item Image 4: scaling the tikzpicture down fixes the issue, but this requires knowledge that 10'' is the largest coordinate in the tikzpicture environment
\begin{choices}{
\begin{tikzpicture}[scale = 3.5/10]
\draw (0,0) rectangle (10,10);
\draw (5,5) node {\textbullet}; % node placed in center of square
\end{tikzpicture}
}
\item Choice 1
\item Choice 2
\item Choice 3
\item Choice 4
\item Choice 5
\end{choices}

\clearpage

\item Image 5: ideally, images 5, 6, \& 7 would look identical as they have the same coordinates, just scaled up and down.
\begin{choices}{
\begin{tikzpicture}
\draw[thick] (0,0)--(1,.5);
\draw[thick] (0,0)--(.5,-1);
\draw[thick] (0,0)--(-1,.5);
\draw[thick] (0,0)--(-.5,-1);
\draw[thick] (0,0)--(1,-.5);
\draw[thick] (0,0)--(-.5,1);
\draw (.05,.2) node {$1$};
\draw (-.25,.25) node {$2$};
\draw (-.2,-.05) node {$3$};
\draw (0,-.25) node {$4$};
\draw (.25,-.25) node {$5$};
\draw (.25,0) node {$6$};
\end{tikzpicture}
}
\item Choice 1
\item Choice 2
\item Choice 3
\item Choice 4
\item Choice 5
\end{choices}

\item Image 6: all coordinates are doubled from Image 5.
\begin{choices}{
\begin{tikzpicture}
\draw[thick] (0,0)--(2,1);
\draw[thick] (0,0)--(1,-2);
\draw[thick] (0,0)--(-2,1);
\draw[thick] (0,0)--(-1,-2);
\draw[thick] (0,0)--(2,-1);
\draw[thick] (0,0)--(-1,2);
\draw (.1,.4) node {$1$};
\draw (-.5,.5) node {$2$};
\draw (-.4,-.1) node {$3$};
\draw (0,-.5) node {$4$};
\draw (.5,-.5) node {$5$};
\draw (.5,0) node {$6$};
\end{tikzpicture}
}
\item Choice 1
\item Choice 2
\item Choice 3
\item Choice 4
\item Choice 5
\end{choices}

\item Image 7: all coordinates are multiplied by a factor of 5 from Image 6. (Note that the nodes are extemely tiny here).
\begin{choices}{
\begin{tikzpicture}
\draw[thick] (0,0)--(10,5);
\draw[thick] (0,0)--(5,-10);
\draw[thick] (0,0)--(-10,5);
\draw[thick] (0,0)--(-5,-10);
\draw[thick] (0,0)--(10,-5);
\draw[thick] (0,0)--(-5,10);
\draw (.5,2) node {$1$};
\draw (-2.5,2.5) node {$2$};
\draw (-2,-.5) node {$3$};
\draw (0,-2.5) node {$4$};
\draw (2.5,-2.5) node {$5$};
\draw (2.5,0) node {$6$};
\end{tikzpicture}
}
\item Choice 1
\item Choice 2
\item Choice 3
\item Choice 4
\item Choice 5
\end{choices}

\end{enumerate}

\end{document}


• I don't have a specific answer, I'm afraid, but I'll just add that textpos 1.9.1 has recently been released, which allows one to adjust the default box reference position (which sounds like it's relevant to this question), and incidentally includes an experimental fix to address a TikZ compatibility issue (which sounds like it might be tangentially relevant to this question). Jun 27, 2019 at 9:40
• @NormanGray, thanks for the reference, I'll have to take a look at version 1.9.1. Jun 27, 2019 at 22:07
• If you want center a tikzpicture inside a textblock(the textpos environment) you can put the tikzpicture inside a figure environment (\begin{figure}\begin{tikzpicture}...\end{tikzpicture}\end{figure}). Jul 11, 2019 at 9:07

If you want to have images automatically resized to fit in the frames while preserving their aspect ratio, then the following should do what you want. Note that the body of the questionpicture environmment is boxed as with lrbox; it can contain anything that can be boxed this way: for instance, one or more character tokens, an image included with \includegraphics, a tikzpicture, etc.).

\documentclass{article}
\usepackage{graphicx}
\usepackage{calc} % for \settototalheight (used for convenience)
\usepackage{xcolor}
\usepackage{xurl}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{calc}

% Frame dimensions
\newlength{\mywidth}
\newlength{\myheight}
\setlength{\mywidth}{3.5cm}
\setlength{\myheight}{3.5cm}

\makeatletter

% Use l3keys to support a key/value-style interface for the optional argument
% of the 'choices' environment (l3keys is great!).
\ExplSyntaxOn

\msg_new:nnn { ryanjform } { duplicate-figure-id }
{ duplicate~figure~identifier:~'\exp_not:n {#1}'. }

% Whether to perform the \scalebox-based autoscaling for a given figure
\bool_new:N \l__ryanjform_do_autoscale_pic_in_choices_bool
% Sequence recording all figure identifiers (for the 'scale to max size' TikZ
% style) found so far
\seq_new:N \g__ryanjform_scale_to_max_style_figure_ids_seq

% Define the options supported in the optional argument of the 'choices'
% environment
\keys_define:nn { ryanjform }
{
autoscale .bool_set:N = \l__ryanjform_do_autoscale_pic_in_choices_bool,
% Value used when the 'autoscale' key is passed with no value
autoscale .default:n = { true },
autoscale .initial:n = { true }
}

\cs_new_protected:Npn \__ryanjform_set_keys:n #1
{ \keys_set:nn { ryanjform } {#1} }

\cs_new_protected:Npn \__ryanjform_check_unique_id:n #1
{
\seq_if_in:NnTF \g__ryanjform_scale_to_max_style_figure_ids_seq {#1}
{ \msg_error:nnn { ryanjform } { duplicate-figure-id } {#1} }
{ \seq_gput_right:Nn \g__ryanjform_scale_to_max_style_figure_ids_seq {#1} }
}

% Set up aliases using LaTeX2e naming style
\cs_set_eq:NN \ryanjformsetup \__ryanjform_set_keys:n
\cs_set_eq:NN \ryanjform@check@unique@id \__ryanjform_check_unique_id:n

% If-then-else command using the boolean
% \l__ryanjform_do_autoscale_pic_in_choices_bool to choose the branch
\NewDocumentCommand \ryanjform@ifautoscale@enabled { }
{
\bool_if:NTF \l__ryanjform_do_autoscale_pic_in_choices_bool
}

\ExplSyntaxOff

\newsavebox{\ryanjform@box}     % will be set with \global
\newlength{\ryanjform@total@height}
\newcounter{choice}[enumi]
% In case you want to prepend the question number to the choice number in
% \thechoice, you can use \renewcommand{\thechoice}{\theenumi.\arabic{choice}}
\renewcommand{\thechoice}{\arabic{choice}} % only the choice number

\newenvironment{questionpicture}{%
\begin{lrbox}{0}}{%
\end{lrbox}%
\global\setbox\ryanjform@box=\box0
}

\AfterEndEnvironment{questionpicture}{%
\setcounter{choice}{0}%
\par\noindent
\setlength{\fboxsep}{0pt}%
\begingroup
\minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep}
\begingroup
\enumerate
\let\ryanjform@item@cmd@orig\item
\let\item\ryanjform@item@cmd
\ignorespaces
}

\let\ryanjform@start@question@picture\questionpicture
\newcommand*{\ryanjform@invalid@place@for@calling@questionpicture}{%
\errmessage{The 'questionpicture' environment must be used at the start of a
'choices' environment}%
}
% Generate an error message unless \questionpicture is used where expected
\let\questionpicture\ryanjform@invalid@place@for@calling@questionpicture

\newenvironment{choices}[1][]{%
\ryanjformsetup{#1}%
\let\questionpicture\ryanjform@start@question@picture
\ignorespaces
}{%
% Restore \item as it was before the 'choices' environment (re)defined it
\let\item\ryanjform@item@cmd@orig
\unskip\ryanjform@hrule       % rule at the end of the last choice
\endenumerate
\endgroup
\endminipage
\endgroup
%
\settototalheight{\ryanjform@total@height}{\usebox{\ryanjform@box}}%
\pgfmathsetmacro{\ryanjform@x@ratio}{\the\mywidth / \wd\ryanjform@box}%
\pgfmathsetmacro{\ryanjform@y@ratio}{\the\myheight / \ryanjform@total@height}%
\pgfmathsetmacro{\ryanjform@scale}{min(\ryanjform@x@ratio,
\ryanjform@y@ratio)}%
%
\fcolorbox{red!20}{blue!20}{%
\begin{minipage}[c][\myheight][c]{\mywidth}
\centering
\ryanjform@ifautoscale@enabled{%
% We know that \ryanjform@scale will be (fully) expanded:
% <https://tex.stackexchange.com/q/497769/73317>
\scalebox{\ryanjform@scale}{\usebox{\ryanjform@box}}%
}{%
\usebox{\ryanjform@box}%
}%
\end{minipage}%
}%
\ignorespacesafterend
}

\newcommand*{\ryanjform@item@cmd}{%
% Automatically insert the horizontal rule unless this is the first \item
% in the current 'choices' environment
\ifnum\value{choice}=0\else\unskip\ryanjform@hrule\fi
\refstepcounter{choice}%
\ryanjform@item@cmd@orig
}

% Autoscaling technique that doesn't affect font sizes in TikZ pictures.
% (based on code from marmot: <https://tex.stackexchange.com/a/497749/73317>)
%
% #1: unique per-picture id allowing several pictures to use this mechanism
%     in a given document (it should contain no control sequence token nor
%     active character)
% #2: width of the reference rectangle
% #3: height of the reference rectangle
\newcommand*\ryanjform@ExportBB[3]{%
\path let
\p1=($(current bounding box.north east)-(current bounding box.south west)$),
\n1={#2/\x1},\n2={#3/\y1}
in \pgfextra{\pgfmathsetmacro{\ryanjform@figscale}{min(\n1,\n2)}%
\expandafter\xdef\csname ryanjform@auto@figscale@#1\endcsname{%
\ryanjform@figscale}};
\immediate\write\@mainaux{%
\string\expandafter
\gdef\string\csname\space ryanjform@auto@figscale@#1\string\endcsname{%
\csname ryanjform@auto@figscale@#1\endcsname}}}

\tikzset{scale to max size/.style args={id #1 width #2height #3}{%
execute at end picture={\ryanjform@ExportBB{#1}{#2}{#3}},
/utils/exec={\ryanjform@check@unique@id{#1}%
\ifcsname ryanjform@auto@figscale@#1\endcsname
\wlog{Found autoscale value for picture '#1'}%
\else
for picture '#1'.}
\expandafter\gdef
\csname ryanjform@auto@figscale@#1\endcsname{1}
\fi},
scale=\csname ryanjform@auto@figscale@#1\endcsname},
form autoscale/.style={%
scale to max size=id #1 width \mywidth height \myheight}}
% End of the code based on <https://tex.stackexchange.com/a/497749/73317>

\newcommand*{\ryanjform@hrule}{%
\leavevmode
\unskip\kern 0.5em
\leaders\hrule height 3pt depth -2.6pt \hfill
\kern 0.5em
}

\makeatother

% Sample pictures only used to show how to use the 'choices' environment
\newcommand*{\sometikzpictureBase}[1]{%
\begin{tikzpicture}#1
\coordinate (A) at (1,5);
\coordinate (B) at (3,15);
\node[below left] at (A) {$A$};
\node[above right] at (B) {$B$};
% Some rules may disappear if one doesn't substract some “epsilon”, see
% <https://tex.stackexchange.com/q/13834/73317>
\draw ($(A)-(0.001, 0.001)$) grid (B);
\end{tikzpicture}%
}

\newcommand*{\sometikzpicture}{\sometikzpictureBase{}}
\newcommand*{\sometikzpictureWithMarmotScaling}[1]{%
\sometikzpictureBase{[form autoscale={#1}]}%
}

\begin{document}

\begin{enumerate}
\item Showing the box I want the image to appear in for the examples below.
\begin{choices}
\begin{questionpicture}
\includegraphics{example-image}
\end{questionpicture}
\item Choice \thechoice
\item Choice \thechoice
\item Choice \thechoice
\item Choice \thechoice
\item Choice \thechoice
\end{choices}

\item Second question:
\begin{choices}
\begin{questionpicture}
\begin{tikzpicture}
\draw (0,0) grid (2,3);
\end{tikzpicture}
\end{questionpicture}
\item Choice \thechoice
\item Choice \thechoice
\item Choice \thechoice
\item Choice \thechoice
\item Choice \thechoice
\end{choices}

\item Third question:
\begin{choices}
\begin{questionpicture}
\tikz \draw (0,0) grid (3,2);
\end{questionpicture}
\item Choice \thechoice
\item Choice \thechoice
\item Choice \thechoice
\end{choices}

\item Fourth question using \verb|autoscale=false| in the optional argument of
the \verb|choices| environment:
\begin{choices}[autoscale=false]
\begin{questionpicture}
\tikz \draw (0,0) grid (3,2);
\end{questionpicture}
\item Choice \thechoice
\item Choice \thechoice
\item Choice \thechoice
\end{choices}

\newpage
\item Using \verb|\scalebox|-based autoscaling (default behavior of
the \verb|choices| environment):
\begin{choices}
\begin{questionpicture}
\sometikzpicture
\end{questionpicture}
\item Choice \thechoice
\item Choice \thechoice
\end{choices}

\item Same picture using Ti\emph{k}Z scaling (scale factor found using
marmot's technique at \url{https://tex.stackexchange.com/a/497749/73317},
via the \verb|form autoscale| style implemented in my answer; note that
several compilation runs may be necessary to let the computed scale factor
converge):
\begin{choices}[autoscale=false]
\begin{questionpicture}
\sometikzpictureWithMarmotScaling{grid-2}
\end{questionpicture}
\item Choice \thechoice
\item Choice \thechoice
\end{choices}

\item A triangle also scaled using the \verb|form autoscale| style:
\begin{choices}[autoscale=false]
\begin{questionpicture}
\begin{tikzpicture}[form autoscale={my triangle}]
\draw (0,0) node[below left] {$A$}-- (0.5,0.5) node[above] {$B$} --
(1,0) node[below right] {$C$} -- cycle;
\end{tikzpicture}
\end{questionpicture}
\item Choice \thechoice
\item Choice \thechoice
\end{choices}

\item Colored and verbatim material inside the framed box:
\begin{choices}
\begin{questionpicture}
\end{tikzpicture}
\end{questionpicture}
\item Choice
\item Choice
\item Choice
\item Choice
\item Choice
\end{choices}

\end{problems}

\end{document}