diff options
Diffstat (limited to 'macros/latex/contrib/interactiveanimation/interactiveanimation.sty')
-rw-r--r-- | macros/latex/contrib/interactiveanimation/interactiveanimation.sty | 596 |
1 files changed, 596 insertions, 0 deletions
diff --git a/macros/latex/contrib/interactiveanimation/interactiveanimation.sty b/macros/latex/contrib/interactiveanimation/interactiveanimation.sty new file mode 100644 index 0000000000..c53d28d7b6 --- /dev/null +++ b/macros/latex/contrib/interactiveanimation/interactiveanimation.sty @@ -0,0 +1,596 @@ +% File `interactiveanimation.sty' + +% Copyright 2012--2013 Luis González, Javier Toro, Pedro Linares + +% This material is subject to the LaTeX Project Public License. See +% http://www.ctan.org/tex-archive/help/Catalogue/licenses.lppl.html +% for the details of that license. + +% This package provides an interface to create Portable Document +% Format (PDF) files with branching and button controllable +% animations. + +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{interactiveanimation}[2013/05/21] + +% Check if pdfTeX is running in PDF mode +\RequirePackage{ifpdf} +\ifpdf +\else + \PackageWarningNoLine{interactiveanimation}{% + Loading aborted, because pdfTeX is not running in PDF mode% + }% + \expandafter\endinput +\fi +\RequirePackage{keyval} + +% Package options +\DeclareOption*{% + \PackageWarning{interactiveanimation}{% + Unknown option `\CurrentOption'}} +\ProcessOptions\relax + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% String and Integer Variables % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def\inta@JSFile{javascript/DocLevel.js}% The name of JavaScript file +\def\inta@JSCode{}% JavaScript code that will be executed at first level +\def\inta@appearanceNames{}% Unique names for inserted images +\def\inta@fields{}% PDF fields array +\def\inta@buttonActions{}% Actions to be performed when a button is pressed +\newcount\inta@numanims\inta@numanims=0% Number of inserted animation widgets +\newcount\inta@numframes\inta@numframes=0% Number of inserted animation frames +\newcount\inta@numbuttons\inta@numbuttons=0% Number of inserted control buttons +\newcount\inta@numimages\inta@numimages=0% Number of inserted external images +\newif\ifinta@animation\inta@animationfalse% Flag to check if is inside of an animation +\newif\ifinta@aframe\inta@aframefalse% Flag to check if is inside of an aframe +\newcount\inta@notequal +\newcount\inta@I +\newcount\inta@loopStart +\newcount\inta@loopEnd +\newcount\inta@step + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Internal Helper Functions % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% inserts string #2 at the end of #1 +\def\inta@concat#1#2{% + \expandafter\xdef + \csname inta@#1\endcsname + {\csname inta@#1\endcsname#2} +} + +% add JavaScript code to te code that is being built +\def\inta@addJS#1{% + \inta@concat{JSCode}{#1} +} + +% Add a field to the fields array +\def\inta@addField{ + \inta@concat{fields}{\the\pdflastannot\space 0 R } +} + +% add appearance name for an inserted image; #1: name, #2: reference number +\def\inta@addAP#1#2{ + \inta@concat{appearanceNames}{ (#1) \the#2 0 R } +} + +% Insert an action given in #2 for a button given in #1 +\def\inta@addAction#1#2{% + \inta@concat{buttonActions}{#1=#2,} +} + +% Tests if argument is a non-negative number +\def\inta@isNumber#1{% + \ifnum9<1#1\space% Non-negative number + 1% + \else% Negative or invalid number + 0% + \fi +} + +% Check if a string is empty or only contains spaces +\def\inta@isEmpty#1{% + \ifnum\pdfstrcmp{#1}{\space}=0 + 1 + \else\ifnum\pdfstrcmp{#1}{\space\space}=0 + 1 + \else\ifnum\pdfstrcmp{#1}{\@empty}=0 + 1 + \else + 0 + \fi\fi\fi +} + +% Extract option and argument from expression option=argument +\def\inta@extractOptArg#1=#2\relax{% + \xdef\inta@opt{#1} + \xdef\inta@arg{#2} +} + +% Removes spaces at the beginning of a string +\def\inta@removeSpaces#1{% + \if\space#1\else + #1\fi +} + +% Transform a number in a three digits number filling with zeros +\def\inta@threeDigits#1{% + \ifnum#1<10 + 00% + \else\ifnum#1<100 + 0% + \fi\fi + \the#1% +} + +% Insert an object given in #2 into the PDF and saves its reference in #1 +\def\inta@insertObject#1#2{% + \immediate\pdfobj{#2}% + \expandafter\xdef + \csname inta@obj:#1\endcsname + {\the\pdflastobj \space 0 R} +} + +% Insert an external image and saves its name in \inta@imageName. #1: file name, #2: page +\def\inta@insertImage#1#2{ + % Cheks if name and page are valid + \edef\inta@filePage{#2} + \IfFileExists{#1}% + { + \ifnum\inta@isEmpty{#2}=1 % if filePage not specified, first one is silently assumed + \def\inta@filePage{1} + \else\ifnum\inta@isNumber{#2}=0 % Not a valid number + \PackageError{interactiveanimation}{% + `#2' of file `\inta@fileName' is not a valid page number% + }{ + A page number must be a positive integer + } + \def\inta@filePage{1} + \else\ifnum#2=0 % Zero-based numbering page + \PackageWarning{interactiveanimation}{% + Page numbers starts in number one% + \MessageBreak% + Page number 1 (first page) was assumed.% + } + \def\inta@filePage{1} + \fi\fi\fi + \@ifundefined{inta@img:#1,\inta@filePage}% Only inserts if was not previously inserted + { + \immediate\pdfximage% + page \inta@filePage {#1} + \expandafter\xdef + \csname inta@img:#1,\inta@filePage\endcsname + {image\inta@threeDigits{\inta@numimages}} + \inta@addAP% + {image\inta@threeDigits{\inta@numimages}}% + {\pdflastximage} + \global\advance\inta@numimages by 1 + }{ + } + \xdef\inta@imageName{% + \csname inta@img:#1,\inta@filePage\endcsname} + }{% File is invalid or not specified + \ifnum\inta@isEmpty{\inta@fileName}=0% Specified but invalid + \PackageError{interactiveanimation}{% + Could not find file `\inta@fileName'% + } + {} + \fi + } +} + +% Refer an object previowsly inserted using \inta@insertObject +\def\inta@refObj#1{% + \csname inta@obj:#1\endcsname} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Initial PDF Setups % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% An AcroForm dictionary is inserted in the catalog, +% containing the Fields array +\pdfobj reserveobjnum +\newcount\inta@fieldsnum +\inta@fieldsnum=\pdflastobj +\pdfcatalog{% + /AcroForm << + /Fields \the\inta@fieldsnum\space 0 R + /NeedAppearances true + >> +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Command and Environment Definitions % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Inserts an animation widget +\newcount\inta@animationWidth\newcount\inta@animationHeight +\newdimen\inta@dimen\inta@dimen=0pt% Auxiliar dimension +\newenvironment{animation}[2]{% Two arguments: width and height + \ifinta@animation% if inside of another animation + \PackageError{interactiveanimation}{% + You can not define an animation inside% + \MessageBreak% + another one. + } + {} + \fi + \inta@animationtrue + \xdef\inta@animationName{animation\the\inta@numanims} + \inta@dimen=#1% Avoid printing measure, e.g. `pt' + \divide\inta@dimen by 65536% Converts to pt + \global\inta@animationWidth=\inta@dimen% store width + \inta@dimen=#2 + \divide\inta@dimen by 65536 + \global\inta@animationHeight=\inta@dimen + \strut\vskip #2% Displaying of widget start from lower left corner + + % inserts a dummy widget for reserving its dimentions and position + \hbox{\pdfannot + width #1 height #2 {% + /Subtype /Widget /FT /Btn + /Ff 65536 % Not necessary, but avoids some warnings + /T (\inta@animationName) + }} + \inta@addField + + \inta@addJS{ + var \inta@animationName = extendField(this.getField("\inta@animationName")); + } +}% +{ + \inta@animationfalse + \inta@addJS{\inta@animationName.displayFirstFrame();} + + % Process button's actions + \@for \inta@tmp :=\inta@buttonActions\do% + { + \ifnum\pdfmatch{=}{\inta@tmp}=1\relax% Is an action + \expandafter\inta@extractOptArg\inta@tmp\relax + \def\inta@buttonName{\inta@opt} + + % Action = previous frame + \ifnum\pdfstrcmp{previous}{\inta@arg}=0 + \inta@addJS{ + \inta@buttonName.targetAction = function() + { \inta@animationName.displayPreviousFrame(); }; + } + + % Action = next frame + \else\ifnum\pdfstrcmp{next}{\inta@arg}=0 + \inta@addJS{ + \inta@buttonName.targetAction = function() + { \inta@animationName.displayNextFrame(); }; + } + + % Action = first frame + \else\ifnum\pdfstrcmp{first}{\inta@arg}=0 + \inta@addJS{ + \inta@buttonName.targetAction = function() + { \inta@animationName.displayFirstFrame(); }; + } + + % Action = last frame + \else\ifnum\pdfstrcmp{last}{\inta@arg}=0 + \inta@addJS{ + \inta@buttonName.targetAction = function() + { \inta@animationName.displayLastFrame(); }; + } + + % Action = given frame name + \else\@ifundefined{inta@frm:\inta@arg}% undefined frame name + { + \PackageError{interactiveanimation}{% + There is no frame named `\inta@arg'% + }{% + Please specify a valid frame name% + Or use: `previous', `next', `first' or `last'} + }{ + \def\inta@frameNumber{\csname inta@frm:\inta@arg\endcsname} + \inta@addJS{ + \inta@buttonName.targetAction = function() + { \inta@animationName.displayFrame("frame\inta@frameNumber");}; + } + }\fi\fi\fi\fi + \fi + } + \global\advance\inta@numanims by 1 + \gdef\inta@buttonActions{}% It is cleared just in case it will be used again +} + +% Inserts a frame to be displayed by the animation widget +\newenvironment{aframe}[2]{% Two arguments: frame name and image + \ifinta@animation\else% if outside an animation + \PackageError{interactiveanimation}{% + You can not define an aframe outside% + \MessageBreak% + an animation. + } + {} + \fi + \ifinta@aframe% if inside another aframe + \PackageError{interactiveanimation}{% + You can not define an aframe inside% + \MessageBreak% + another one. + } + {} + \fi + + \inta@aframetrue + \xdef\inta@frameName{frame\the\inta@numframes} + \vskip-\baselineskip + + \hbox{\pdfannot{% + /Subtype /Widget /FT /Btn + /Ff 65537% No user interaction + /T (\inta@frameName) + /BS << /W 0 >>% No border is drawn + /MK << /BG [1]% White background color + /IF << /S /A >> % Non-proportional scaling + /TP 1 >>% Only image, no text caption + }} + \inta@addField + + \inta@addJS{% + var \inta@frameName = extendField(this.getField("\inta@frameName")); + \inta@animationName.addFrame(\inta@frameName); + } + + % Animation frame image + \inta@I=0 + \gdef\inta@fileName{}\gdef\inta@filePage{} + \@for \inta@tmp :=#2\do% Iterates each element in the comma separated list + { + \ifnum\inta@I=0% File name + \edef\inta@fileName{\inta@tmp} + \else\ifnum\inta@I=1% File page + \edef\inta@filePage{\inta@removeSpaces{\inta@tmp}} + \fi\fi + \advance\inta@I by 1 + } + + % Image is inserted, if it is all right associates it with frame + \inta@insertImage{\inta@fileName}{\inta@filePage} + \ifnum\inta@isEmpty{\inta@imageName}=0 + \inta@addJS{% + \inta@frameName.buttonSetIcon(this.getIcon("\inta@imageName")); + \inta@frameName.buttonPosition = position.iconOnly; + } + \fi + + % Saves number frame for actual frame's name + \expandafter\xdef\csname inta@frm:#1\endcsname + {\the\inta@numframes} + \global\advance\inta@numframes by 1 +}% +{ + \inta@aframefalse +} + +% Definitions for keyval needed +\define@key{inta}{position}{#1} + +% option=position +\define@key{inta}{position}{% + % Check if option has a valid argument + \ifnum\pdfmatch{#1}{ left right above below center from }=1 + \inta@addJS{\inta@buttonName .relatPosition = option.#1;} + \else% if it is invalid, does not process option and display an error + \PackageError{interactiveanimation}{% + `#1' is not a valid position.}{% + Valid positions are:% + \MessageBreak% + `left', `right', `above', `below' and `from'} + \fi +} +% option=X +\define@key{inta}{X}{% + \ifnum\inta@isNumber{#1}=1% if is a non-negative integer + % Defines the coordinate depending on the animation width + % so the animation can be resized + \inta@addJS{% + \inta@buttonName.pointX = #1 * \inta@animationName.width / \the\inta@animationWidth; + } + \else\ifnum\pdfmatch{#1}{ start center end }=1 + \inta@addJS{\inta@buttonName .positionX = option.#1;} + \else% if it is invalid, does not process option and displays an error + \PackageError{interactiveanimation}{% + `#1' is not a valid X coordinate}{% + Valid X coordinate are:% + \MessageBreak% + A non-negative integer, or `start', `center' or `end'} + \fi\fi +} +% option=Y +\define@key{inta}{Y}{% + \ifnum\inta@isNumber{#1}=1% if is a non-negative integer + \inta@addJS{% + \inta@buttonName.pointY = #1 * \inta@animationName.height / \the\inta@animationHeight; + } + \else\ifnum\pdfmatch{#1}{ start center end }=1 + \inta@addJS{\inta@buttonName .positionY = option.#1;} + \else% if it is invalid, does not process option and displays an error + \PackageError{interactiveanimation}{% + `#1' is not a valid Y coordinate}{% + Valid Y coordinate are:% + \MessageBreak% + A non-negative integer, or `start', `center' or `end'} + \fi\fi +} +% option=scale +\define@key{inta}{scale}[1]{% + \inta@addJS{\inta@buttonName.scale(#1);} +} +% option=hidden +\define@key{inta}{hidden}[true]{% + \inta@addJS{\inta@buttonName.toggleHidden = true;} +} +% option=transparent +\define@key{inta}{transparent}[true]{% + \inta@addJS{ \inta@buttonName.transparent = true;} +} +% option=width +\define@key{inta}{width}{% + \inta@addJS{% + \inta@buttonName.width = #1 * \inta@animationName.width / \the\inta@animationWidth; + } +} +% option=height +\define@key{inta}{height}{% + \inta@addJS{% + \inta@buttonName.height = #1 * \inta@animationName.height / \the\inta@animationHeight; + } +} +% option=span +\define@key{inta}{span}[1000]{% + \inta@addJS{\inta@buttonName.timeSpan = #1;} +} +% option=keep +\define@key{inta}{keep}[true]{% + \inta@addJS{\inta@buttonName.keep = true;} +} + +% Insert a button for interacting with the animation +\newdimen\inta@buttonWidth +\newcommand{\controlbutton}[4][]{% Four arguments: [animation], button's caption, destination frame and specification in a comma separated list + \ifinta@animation\else% if outside an animation + \PackageError{interactiveanimation}{% + You cannot define a control button% + \MessageBreak% + outside an animation. + } + {} + \fi + \edef\inta@buttonName{button\the\inta@numbuttons} + \setbox0=\hbox{ #2 \strut} + \inta@buttonWidth=\wd0 + \vskip-\baselineskip + + % Button is inserted + \hbox{\pdfannot + width \inta@buttonWidth{% + /Subtype /Widget /FT /Btn% Button field + /Ff 65536% Push button type + /T (\inta@buttonName)% Button's name, accessible from JavaScript + /BS << /S /B >>% Beveled border style + /MK << /BG [0.6]% 60% white background color + /CA (#2) >>% Caption + /H /P% Push highlighting mode + /A << /S /JavaScript /JS (% JavaScript action to be performed + \inta@buttonName.buttonAction(); + ) >> + }\strut} + \inta@addField + + \inta@addJS{ + var \inta@buttonName = extendField(this.getField("\inta@buttonName")); + \inta@buttonName.images = new Array(); + } + + % If present, button's action is stored and will be processed at end of animation environment + \ifnum\inta@isEmpty{#3}=0 + \inta@addAction{\inta@buttonName}{#3} + \else + \inta@addJS{ + \inta@buttonName.targetAction = function() + { \inta@animationName.displayFrame("\inta@frameName");}; + } + \fi + + % Button's options are processed + \setkeys{inta}{#4} + + % Button's image sequence is processed + \def\inta@rangeStart{}\def\inta@rangeEnd{} + \def\inta@fileName{} + \inta@I=0 + \@for \inta@tmp :=#1\do + { + \ifnum\inta@I=0% File name + \edef\inta@fileName{\inta@tmp} + \else\ifnum\inta@I=1% Start range + \edef\inta@rangeStart{\inta@removeSpaces{\inta@tmp}} + \else\ifnum\inta@I=2% End range + \edef\inta@rangeEnd{\inta@removeSpaces{\inta@tmp}} + \fi\fi\fi + \advance\inta@I by 1 + } + % Check if ranges are specified and valid + \ifnum\inta@isEmpty{\inta@fileName}=0 + \ifnum\inta@isEmpty{\inta@rangeStart}=1 + \def\inta@rangeStart{1} + \def\inta@rangeEnd{1} + \else\ifnum\inta@isNumber{\inta@rangeStart}=0 + %\inta@insertImage will display the appropriate error + \inta@insertImage{\inta@fileName}{\inta@rangeStart} + \def\inta@rangeStart{1} + \def\inta@rangeEnd{1} + \fi\fi + \ifnum\inta@isEmpty{\inta@rangeEnd}=1% if only one number was given, set range to (1,number) + \edef\inta@rangeEnd{\inta@rangeStart} + \def\inta@rangeStart{1} + \else\ifnum\inta@isNumber{\inta@rangeEnd}=0 + %\inta@insertImage will display the appropriate error + \inta@insertImage{\inta@fileName}{\inta@rangeEnd} + \edef\inta@rangeEnd{\inta@rangeStart} + \def\inta@rangeStart{1} + \fi\fi\fi + + % If was given an animation action for the button, all animation frames are inserted + \ifnum\inta@isEmpty{\inta@fileName}=0 + \inta@notequal=1% End loop flag + \inta@loopStart=\inta@rangeStart + \inta@loopEnd=\inta@rangeEnd\relax + \ifnum\inta@loopStart>\inta@loopEnd% Descending order + \inta@step=-1 + \else + \inta@step=1 + \fi + \inta@I=\inta@loopStart\loop + \inta@insertImage{\inta@fileName}{\the\inta@I} + \inta@addJS{ + \inta@buttonName.addImage("\inta@imageName"); } + \ifnum\inta@I=\inta@loopEnd + \inta@notequal=0% Ends loop + \fi + \advance\inta@I by \inta@step + \ifnum\inta@notequal=1\repeat + \fi + + \inta@addJS{\inta@frameName.addButton(\inta@buttonName);} + \global\advance\inta@numbuttons by 1 +} + +% JavaScript code is inserted into the PDF +\input\inta@JSFile\relax +\AtEndDocument{\inta@insertObject{JavaScript}{ + << /S /JavaScript /JS ( + \inta@JSCode + ) >> +}} + +% A reference to the JavaScript code is inserted intho the names dictionary, +% so the JavaScript actions perform when the document is opened +\AtEndDocument{\pdfnames{ + /JavaScript << + /Names [ (interactiveanimation) \inta@refObj{JavaScript} ] >> +}} + +% Appearance names dictionary is inserted into the names dictionary,% +% so it is posible accessing inserted images from JavaScript +\AtEndDocument{\pdfnames{ + /AP << + /Names [\inta@appearanceNames] >> +}} + +% Array fields is inserted +\AtEndDocument{\immediate\pdfobj useobjnum \inta@fieldsnum {% + [\inta@fields]% +}} + |