[Tex/LaTex] fit tikzpicture to page width or height (using macros)

macrostikz-pgftikz-trees

I have a new environment defined that fits tikzpicture to the page width,
but when tikzpicture is long in height the bottom part of the tikzpicture is cut and extra blank page appears because of the jump of the tikzpicture to the next page.
enter image description here

I want to find scalewidth and scaleheight parameters (a scale ration to fit page width and a scale ration to fit page height, respectively) and choose the smaller one for scaling in order to guarantee full appearance of tikzpicture.

Could you tell me how to modify the scaletikzpicturetowidth environment to achieve this?

  \NewEnviron{scaletikzpicturetowidth}[1]{%
  \def\tikz@width{#1}     % what does this do?
  \def\tikzscale{1}   %declares tikzscale parameter 
  \begin{lrbox}{\measure@tikzpicture}    % BODY is saved in measure@tikzpicture
      \BODY
  \end{lrbox}%
  \pgfmathparse{#1/\wd\measure@tikzpicture}    % a scale ratio = page_width/tikzpic_width
  \edef\tikzscale{\pgfmathresult}  % tikzscale = a scale ration 
  \BODY
}

So, the environmet will take two arguments:

#1 = \textwidth and #2 = \textheight,

then it calculates

widthscale = #1/\wd\measure@tikzpicture and

heightsacle = #2/\ht\measure@tikzpicture, and

\tikzscale = min(widthscale, heightsacle)

\documentclass[landscape]{article}
\setlength\textwidth{10in}
\setlength\textheight{8.5in}
\setlength\oddsidemargin{-.5in}
\setlength\evensidemargin{-.5in}
\setlength\topmargin{-.7in}
\setlength\headsep{0in}
\setlength\headheight{0in}
\setlength\topskip{0in}
\usepackage{tikz-qtree-compat}
\usepackage{environ}
\makeatletter
\newsavebox{\measure@tikzpicture}
\NewEnviron{scaletikzpicturetowidth}[1]{%
  \def\tikz@width{#1}%
  \def\tikzscale{1}\begin{lrbox}{\measure@tikzpicture}%
  \BODY
  \end{lrbox}%
  \pgfmathparse{#1/\wd\measure@tikzpicture}%
  \edef\tikzscale{\pgfmathresult}%
  \BODY
}
\makeatother
\begin{document}
  \begin{scaletikzpicturetowidth}{\textwidth}
  \begin{tikzpicture}[scale=\tikzscale, baseline=0pt, grow=down]
    \tikzset{level distance = 25pt, sibling distance = -5pt}
    \tikzset{every tree node/.style={align=center,anchor=north}}
    \Tree
      [. \node{ $s$};
        [. \node{\begin{tabular}{c}
                \texttt{run}\\
                \textbf{run}\\
                \normalsize{na}\\
                \scriptsize{na}\\
                \scriptsize{na}\\
                \end{tabular} }; ]
        [. \node{\begin{tabular}{c}
             \textbf{$X_{3109}$}\\
           \end{tabular} };
        ]
      ]
  \end{tikzpicture}
  \end{scaletikzpicturetowidth}
  \clearpage
  \begin{scaletikzpicturetowidth}{\textwidth}
  \begin{tikzpicture}[scale=\tikzscale, baseline=0pt, grow=down]
    \tikzset{level distance = 25pt, sibling distance = -5pt}
    \tikzset{every tree node/.style={align=center,anchor=north}}
    \Tree
      [. \node{ $np$};
        [. \node{\begin{tabular}{c}
                \texttt{some}\\
                \textbf{some}\\
                \normalsize{na}\\
                \scriptsize{na}\\
                \scriptsize{na}\\
                \end{tabular} }; ]
        [. \node{\begin{tabular}{c}
                \texttt{girl}\\
                \textbf{girl}\\
                \normalsize{na}\\
                \scriptsize{na}\\
                \scriptsize{na}\\
                \end{tabular} }; ]
      ]
  \end{tikzpicture}
  \end{scaletikzpicturetowidth}
  \clearpage
\end{document}

Best Answer

You could probably do this using tikz internal scale, but this is fast and works on anything.

\documentclass{article}
\usepackage{tikz}
\usepackage{graphics}

\newlength{\xsize}
\newlength{\ysize}
\newsavebox{\tempbox}

\newcommand{\maxsize}[1]% #1=image to be scaled
{\savebox{\tempbox}{#1}%
\settoheight{\ysize}{\usebox{\tempbox}}%
\settowidth{\xsize}{\usebox{\tempbox}}%
\pgfmathparse{\xsize/\textwidth > \ysize/\textheight}%
\noindent%
\if\pgfmathresult1\resizebox{\textwidth}{!}{\usebox{\tempbox}}%
\else\centering\resizebox{!}{\textheight}{\usebox{\tempbox}}%
\fi}

\begin{document}
\vspace*{-\baselineskip}% first page only
\maxsize{\rule{3in}{1in}}

\maxsize{\rule{1in}{5in}}

\end{document}

Further study shows that the problem with the first page comes from round off error in \resizebox. Consequently I revised the solution to be more robust. It originally had additional code to test and truncate \MaxScale, but that turned out to be unnecessary.

\documentclass{article}
\usepackage{tikz}
\usepackage{graphics}

\newlength{\MaxWidth}
\newlength{\MaxHeight}
\newsavebox{\MaxBox}

\newcommand{\maxsize}[1]% #1=image to be scaled
{\savebox{\MaxBox}{#1}% get size of box
\settoheight{\MaxHeight}{\usebox{\MaxBox}}%
\settodepth{\MaxWidth}{\usebox{\MaxBox}}%
\addtolength{\MaxHeight}{\MaxWidth}%
\settowidth{\MaxWidth}{\usebox{\MaxBox}}%
\pgfmathdivide{\textwidth}{\MaxWidth}% compute scale for width
\let\MaxScale=\pgfmathresult%
\ifdim\MaxScale\MaxHeight>\textheight% compute scale for height
\pgfmathdivide{\textheight}{\MaxHeight}
\let\MaxScale=\pgfmathresult%
\fi%
\noindent\centering\scalebox{\MaxScale}{\usebox{\MaxBox}}}

\begin{document}

\maxsize{\rule{1in}{5in}}

\maxsize{\rule{3in}{1in}}

\end{document}