# Automatic “Zoom in” hypertext boxes in PDF

I remember long time ago, seeing PDF documents (such as online PDF versions of newspapers) that had hypertext boxes around paragraphs, such that when you clicked inside those boxes the surrounded paragraph will be zoomed in to exactly that paragraph in the Acrobat Reader window. I am not sure what tool was used to create those PDFs, and unfortunately I can't find one example at the moment.

Is it possible to reproduce such zoom-on-click feature with some pdflatex code? To repeat: the requested feature is to create hypertext boxes around paragraphs or figures that when clicked the PDF viewer would zoom in into that region.

This feature can be useful in PDFs with lots of structure in a given page, such as small paragraphs or figures or figure captions scattered around the page.

(Beamer has a "zoom in" feature (\framezoom) but it is not the same thing because that creates a new page with the zoomed region, and I just want to force the PDF reader to enclose the area in the same page by only zooming. I am also aware of marquee zoom in Acrobat but it is not what I am looking for because it is not aware of the content.)

-

The following code (for use with pdfLaTeX) defines the command \zoombox[box line width]{contents}. It creates a click-to-zoom box for fitting the second argument 'contents' into the AdobeReader window. A dotted line is drawn around the box if the optional 'box line width' is >0.

EDIT: The code has been optimized for presentation PDFs (such as beamer generated ones) looked at in full screen mode. The zoom-to box is now centred on the screen when clicked, as requested by @alfC.

\documentclass{beamer}

\usepackage{graphicx}
\usepackage{mwe}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  \zoombox[box line width]{contents}
%
%  optimized version for beamer: in full screen, zoom boxes are centred
%  in the viewer; useable with any documenclass
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\newsavebox\zb@x
\newcounter{z@@m}
\usepackage{calc}
\newdimen\B@r\newdimen\P@r
\newdimen\@zw\newdimen\@zh\newdimen\@zd

\newcommand{\zoombox}[2][0]{%
\leavevmode%
\sbox\zb@x{#2}%
\setlength\B@r{1pt*\ratio{\wd\zb@x}{\ht\zb@x+\dp\zb@x}}%
\setlength\P@r{1pt*\ratio{\paperwidth}{\paperheight}}%
\ifdim\B@r>\P@r\relax%
\setlength\@zw{\wd\zb@x}\setlength\@zh{\@zw*\ratio{\paperheight}{\paperwidth}}%
\setlength\@zd{(\@zh-\ht\zb@x-\dp\zb@x)*\real{0.5}+\dp\zb@x}%
\setlength\@zh{\@zh-\@zd}%
\else%
\setlength\@zh{\ht\zb@x+\dp\zb@x}%
\setlength\@zw{\@zh*\ratio{\paperwidth}{\paperheight}}%
\setlength\@zh{\ht\zb@x}\setlength\@zd{\dp\zb@x}%
\fi%
\makebox[0pt][l]{\makebox[\wd\zb@x][c]{\makebox[\@zw][l]{%
\pdfdest name {zbfs\thez@@m} fitr
width  \@zw\space
height \@zh\space
depth  \@zd\space
}}}%
\pdfdest name {zb\thez@@m} fitr
width  \wd\zb@x\space
height \ht\zb@x\space
depth  \dp\zb@x\space
\immediate\pdfannot
width  \wd\zb@x\space
height \ht\zb@x\space
depth  \dp\zb@x\space
{%
/Border [0 0 #1 [1 2]]
/A <<
/S/JavaScript
/JS (
if(typeof(zoomed)=='undefined'||!zoomed){
var lastView=this.viewState;
if(app.fs.isFullScreen) this.gotoNamedDest('zbfs\thez@@m');
else this.gotoNamedDest('zb\thez@@m');
zoomed=true;
}else{
this.viewState=lastView;
zoomed=false;
}
)
>>
}%
\usebox{\zb@x}%
\stepcounter{z@@m}%
}
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
\begin{frame}
\zoombox{\includegraphics[width=1cm]{example-image-a}}
\zoombox{\includegraphics[width=1cm]{example-image-b}}
\zoombox{\includegraphics[width=1cm]{example-image-c}}
\zoombox{\includegraphics[height=1cm]{example-image-golden}}
\zoombox{\includegraphics[width=1cm]{example-image-golden-upright}}
\end{frame}
\end{document}


Pairs of commands for marking opposing corners (lower left & upper right .OR. upper left & lower right) of a zoom box and for marking text sequences/entire paragraphs are defined in the following example.

\documentclass{article}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\InputIfFileExists{\jobname.zom}{}{}
\newwrite\zoomdat
\immediate\openout\zoomdat=\jobname.zom
\newcommand{\startzoombox}[2][0]{%
\leavevmode%
\pdfsavepos%
\protected@write\zoomdat{}{%
\string\expandafter\string\def\string\csname\space zb#2.ulx\string\endcsname{%
\noexpand\number\pdflastxpos}%
\string\expandafter\string\def\string\csname\space zb#2.uly\string\endcsname{%
\noexpand\number\pdflastypos}%
}%
\ifcsname zb#2.ulx\endcsname\ifcsname zb#2.lrx\endcsname%
\edef\zoomwd{\dimexpr \csname zb#2.lrx\endcsname sp- \csname zb#2.ulx\endcsname sp\relax}%
\edef\zoomdp{\dimexpr \csname zb#2.uly\endcsname sp- \csname zb#2.lry\endcsname sp\relax}%
\pdfdest name {zb#2.in} fitr
width \zoomwd
height 0pt
depth \zoomdp
\immediate\pdfannot
width \zoomwd
height 0pt
depth \zoomdp
{%
/Border [0 0 1 [1 2]]
/A <<
/S/JavaScript
/JS (
if(typeof(zoomed)=='undefined'||!zoomed){
var lastView=this.viewState;
zoomed=true;
this.gotoNamedDest('zb#2.in');
}else{
this.viewState=lastView;
zoomed=false;
}
)
>>
}%
\fi\fi%
}
\def\stopzoombox#1{%\leavevmode%
\leavevmode%
\pdfsavepos%
\protected@write\zoomdat{}{%
\string\expandafter\string\def\string\csname\space zb#1.lrx\string\endcsname{%
\noexpand\number\pdflastxpos}%
\string\expandafter\string\def\string\csname\space zb#1.lry\string\endcsname{%
\noexpand\number\pdflastypos}%
}%
}
\def\startzoom{%
\@ifstar\@startzoomstar\@startzoom%
}
\newcommand{\@startzoom}[2][0]{%
\raisebox{\baselineskip}[0pt][0pt]{\startzoombox[#1]{#2}}%
}
\newcommand{\@startzoomstar}[2][0]{%
\makebox[0pt][r]{\raisebox{\baselineskip}[0pt][0pt]{\startzoombox[#1]{#2}}%
\hspace{\parindent}}%
}
\def\stopzoom{%
\@ifstar\@stopzoomstar\@stopzoom%
}
\def\@stopzoom#1{%
\raisebox{-1ex}[0pt][0pt]{\stopzoombox{#1}}%
}
\def\@stopzoomstar#1{%
\hfill\raisebox{-1ex}[0pt][0pt]{\stopzoombox{#1}}%
}
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
\section{Paragraph zooming}
\startzoom[1]{p1}First paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first \startzoom[1]{detail}Interesting detail\stopzoom{detail} first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph first paragraph.\stopzoom*{p1}

\startzoom*[1]{p2}Second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph second paragraph.\stopzoom*{p2}
\end{document}


The two commands for marking the corners of a zoom area are

\startzoombox[line width]{label}


and

\stopzoombox{label}


For marking text and paragraphs the following commands have been provided:

\startzoom[lwidth]{label}
\startzoom*[lwidth]{label}
\stopzoom{label}
\stopzoom*{label}.


The starred versions insert horizontal space (negative \parindent on first line, \hfill at the end of the paragraph).

Note that associated command pairs must be identified by unique labels and that at least two runs of pdfLaTeX are required. Zoom areas may be nested. However, smaller ones should be placed on top of bigger ones (i.e. to appear later in the code), in order not to be obscured.

EDIT:

A dvipdfmx/XeLaTeX version of the \zoombox macro is given in: How do I include a click-to-zoom thumbnail picture in a non-beamer document?

-
(1)results from the fact, that content is stored in a \savebox first in order to be measured its dimensions for later sizing the zoom area. Saveboxes are lr-boxes, that is, they behave like \hbox (TeX) or \makebox (LaTeX). In order to get whole paragraphs, you could either put the text in a \parbox or minipage, as you suggested, or use the \startzoombox[line width]{label} \stopzoombox{label} commands to mark opposing corners of the zoom area. –  AlexG Mar 1 '11 at 9:53
(2) To get the link border colour as defined by hyperref, insert /C [\@linkbordercolor] before /Border ... in the above code. The preamble code between the comment lines must be surrounded by \makeatletter and \makeatother and don't forget to \usepackage{hyperref} –  AlexG Mar 1 '11 at 10:19
1a) You could put \hfill between the last full stop and \stopzoombox. This pushes the latter to the right text margin. –  AlexG Mar 2 '11 at 15:53
@alfC I am afraid it is not possible. I tried the scroll JavaScript method, but instead of centring the given position (the centre coordinates of the zoom-to region) within the viewer window (as advertised by the Acrobat JavaScript specification), it is moved to the upper left corner of the viewer window, only leaving visible the lower right quarter of the zoom-to region, which is clearly not what is expected. –  AlexG Sep 12 '13 at 8:36
@alfC: In presentation pdfs, boxes are centred in full screen mode when clicked. See my edit. –  AlexG Sep 13 '13 at 14:16

AFAIK there is no predefined macro or package which does all of that, but the elements you need exist: You can place hyperlinks with zoom windows using the hyperref package. You can get the coordinates required using the zref package(s), especially zref-abspos. These packages are from the same author and play nicely together.

You would need to place the paragraph in its own box. Place a zref marker before and after the paragraph on the left and right site, respectively to get the coordinates of it. Then put the whole box into a \href macro which uses the coordinates as target. See the manual of hyperref for more details especially \href and the FitR (rectangle zoom).

-
@Scharrer would this allow to also go back to previous zoom level? That is, after FitR is used to enlarge to fit page would for example a second click make document go back to its origin? –  Maesumi Apr 5 '12 at 2:59
@Maesumi: There should be the possibility to add a clickable item which triggers to go back to the last viewpoint. In Adobe Reader this can be done manually using ALT+ARROW LEFT, so there should be an hyperlink way as well. –  Martin Scharrer Apr 5 '12 at 6:42
PDFtex does have the primitives to support article threads: \pdfthread, \pdfstartthread, \pdfendthread, \pdfthreadmargin. But as far as I know the only latex support is rather limited, via the pdfpages, so only useful if you are using pdfpages for some complex imposition.