diff options
Diffstat (limited to 'macros/luatex/generic')
-rw-r--r-- | macros/luatex/generic/pdfextra/LICENSE | 12 | ||||
-rw-r--r-- | macros/luatex/generic/pdfextra/README.md | 41 | ||||
-rw-r--r-- | macros/luatex/generic/pdfextra/examples/pdfextra-example-latex.tex | 223 | ||||
-rw-r--r-- | macros/luatex/generic/pdfextra/examples/pdfextra-example-part.prc | bin | 0 -> 58592 bytes | |||
-rw-r--r-- | macros/luatex/generic/pdfextra/examples/pdfextra-example.pdf | bin | 0 -> 385350 bytes | |||
-rw-r--r-- | macros/luatex/generic/pdfextra/examples/pdfextra-example.tex | 345 | ||||
-rw-r--r-- | macros/luatex/generic/pdfextra/pdfextra-doc.pdf | bin | 0 -> 245952 bytes | |||
-rw-r--r-- | macros/luatex/generic/pdfextra/pdfextra-doc.tex | 772 | ||||
-rw-r--r-- | macros/luatex/generic/pdfextra/pdfextra.opm | 1728 | ||||
-rw-r--r-- | macros/luatex/generic/pdfextra/pdfextra.sty | 4 | ||||
-rw-r--r-- | macros/luatex/generic/pdfextra/pdfextra.tex | 364 |
11 files changed, 3489 insertions, 0 deletions
diff --git a/macros/luatex/generic/pdfextra/LICENSE b/macros/luatex/generic/pdfextra/LICENSE new file mode 100644 index 0000000000..7468558136 --- /dev/null +++ b/macros/luatex/generic/pdfextra/LICENSE @@ -0,0 +1,12 @@ +Copyright (C) 2021 Michal Vlasák <lahcim8@gmail.com> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/macros/luatex/generic/pdfextra/README.md b/macros/luatex/generic/pdfextra/README.md new file mode 100644 index 0000000000..70ef8febf7 --- /dev/null +++ b/macros/luatex/generic/pdfextra/README.md @@ -0,0 +1,41 @@ +# PDF Extra + +PDF Extra provides extra PDF features for OpTeX (or in limited form for plain +LuaTeX and LuaLaTeX). + +As a minimalistic package OpTeX doesn't support "advanced" features of PDF file +format in the base. This third party package aims to provide them. Summary of +supported features: + + - insertion of multimedia (audio, video, 3D); + - hyperlinks and other actions, + - trigger events; + - transitions; + - attachments. + +The repository of PDF Extra is at https://github.com/vlasakm/pdfextra. + +# Documentation + +See `pdfextra-doc.pdf` for documentation. It contains user documentation as +well as technical documentation interleaved with source code. + +# Installation instructions + +Put `pdfextra.opm` where your TeX can find it. Additionaly, for plain/LaTeX you +need `pdfextra.tex`, and on top of that `pdfextra.sty` for LaTeX. + +Or just copy paste anything you want. + +# Usage + +In OpTeX do: `\load [pdfextra]`. + +In plain LuaTeX do: `\input pdfextra`. + +In LuaLaTeX do: `\usepackage{pdfextra}`. + +# License + +This package is available under the Zero-Clause BSD license. See `LICENSE` for +more information. diff --git a/macros/luatex/generic/pdfextra/examples/pdfextra-example-latex.tex b/macros/luatex/generic/pdfextra/examples/pdfextra-example-latex.tex new file mode 100644 index 0000000000..dc5a31f9f1 --- /dev/null +++ b/macros/luatex/generic/pdfextra/examples/pdfextra-example-latex.tex @@ -0,0 +1,223 @@ +% Compiled with: +% env TEXMFDOTDIR='..//:~/src/texlive/texmf-dist/source/latex/media9/files/' lualatex pdfextra-example-latex.tex +\documentclass{article} + +\usepackage{hyperref} +\usepackage{xcolor} +\usepackage{graphicx} +\usepackage{pdfextra} +\sdef{_linkcolor}{\color{blue}} + +\begin{document} + +% Using document level JavaScript we can run initialization code right at the +% start (this is more flexible than \openaction). +\dljavascript[random]{ + function getRandomNumber() { + return 4; % chosen by fair dice roll, ![https://xkcd.com/221/] + } + console.println("initialized with seed " + getRandomNumber()); +} + + + +% For external references we need to `\filedef` "external" and "url" files +% beforehand: +\filedef/u[doc-internet]{http://petr.olsak.net/ftp/olsak/optex/optex-doc.pdf} +\filedef/x[doc-local]{optex-doc.pdf} + +% We then try to reference them using external references: +\hlink[extref:doc-internet:ref:langphrases]{OpTeX documentation, section ``Multilingual phrases and quotation marks''.} +\hlink[extpgref:doc-internet:12]{OpTeX documentation, page 12.} + + + +% Insert a video twice with different settings: +\filedef/e[video]{example-movie.mp4} +% Insert video into page using Renditions mechanism with controls and autoplay. +\render[video][ + controls=true, + aactions=\renditionautoplay, +]{\includegraphics[width=\textwidth]{example-image.pdf}} + +% We set red text color, blue border (RGB triplet) for "rendition" links. +\sdef{_renditionlinkcolor}{\color{red}} +\sdef{_renditionborder}{0 1 0} +% Then we add some buttons. +\hlink[rendition::play]{play} \hlink[rendition::pause]{pause} +\hlink[rendition::stop]{stop} \hlink[rendition::resume]{resume} + +% Render the same file again, but with different dimensions, no controls +% and explicit activation. +\render[video]{\includegraphics{example-image.pdf}} +% This one can't have buttons, because it doesn't have its own name -- "video" +% is used by the previous one. + + + +% Add already embedded video to the list of attachments shown in user +% interface. +\attach[video] + + +% We are done for this page, who not go to the next one? +% +% \hlink typesets in group, so we locally disable lining links (our button is +% bigger than normal line) +\hlink[named:NextPage]{\nolininglinks\scalebox{2}[2]{NEXT PAGE}} + + + +\clearpage + + + +% Deliver fresh random number to everyone who sees this page using "page open" +% ("O") trigger event of page's actions. +\pageactions[ + {O} {js:{app.alert("Random number for you is " + getRandomNumber());}} +] + + +% Link to previous page (predefined PDF action) with "Split" transition: +\hlink[named:PrevPage, transition:Split]{Go to previous page (with transition).} + + + +\clearpage + +% A 3D Rich Media annotation with a custom view and script follows: + +% First we define the 3D file, so we can use same name for the view and have it +% auto included. +\filedef/e[example]{pdfextra-example-part.prc} + +% Then a simplistic "front" view. +\DDDview[example][ + projection=ortho, + roo=400, +] + +% A 3D JavaScript script that will define function to use with action button. +% (we write the script only now, so we don't have to carry around an extra file +% in the package, you would normally have the contents in the file -- "turn.js" +% in this case). +\newwrite\js +\immediate\openout\js=turn.js +% rotate every node by angle degrees along $x$ axis +\immediate\write\js{\detokenize{ +function turn(angle) { + angle = angle * Math.PI / 180; + for (var i = 0; i < scene.nodes.count; i++) { + var node = scene.nodes.getByIndex(i); + node.transform.rotateAboutXInPlace(angle); + } +} +}} +\closeout\js + +% The annotation itself, with our script above. +\RM[example][ + activation=auto, + scripts=turn.js, +]{\textcolor{white}{\vrule width\textwidth height\textwidth}} + +\hlink[js:\DDDcontext{example}.turn(10);]{Turn by 10 degrees along $x$ axis.}\par +\hlink[js:\DDDcontext{example}.turn(30);]{Turn by 30 degrees along $x$ axis.}\par +\hlink[js:\DDDcontext{example}.turn(90);]{Turn by 90 degrees along $x$ axis.} + + + +\clearpage + + +% Here is the "epix" 3D model example from media9 documentation ported to +% pdfextra. +% +% First we define a view with the same name as the future Rich Media +% annotation. This view will then automatically be used if no `views` key is +% specified. No need to be excessive with decimal numbers. +\DDDview[epix][ + FOV=30, + c2c=4 2 3, + coo=4.41 2.19 0, + roo=430, +] + +% 3Dmenu is used often, lets use shortcut instead. +\filedef/e[3dmenu]{3Dmenu.js} +% We want to use the nice "epix" name for the view and annotation. +\filedef/e[epix]{epix.prc} + +% Just to spice things up, we diverge a bit from media9 and define custom +% background color. We can't use LaTeX's colors, so we use RGB triplet. +\newcommand{\GreyBG}{0.9 0.9 0.9} + +% Define the 3D Rich Media annotation. One view is automatically detected from +% name ("epix"). Scripts will run in specified order at initialization time +% (when the annotation is activated). +\RM[epix][ + scripts={ + asylabels.js, + 3Dspintool.js, + 3dmenu, + }, + background=\GreyBG, +]{\includegraphics[width=0.8\textwidth, height=0.8\textwidth]{epixposter.pdf}} + + + +\clearpage + + + +% Second media9 example is "dice" it has a lot of views, but they are simple. +% This time we can't use the view name deduction from `\RM` name -- there is +% more than one view. +\DDDview[Front][roo=27] +\DDDview[Back][roo=27, c2c=0 1 0] +\DDDview[Left][roo=27, c2c=-1 0 0] +\DDDview[Right][roo=27, c2c=1 0 0] +\DDDview[Top][roo=27, c2c=0 0 1] +\DDDview[Bottom][roo=27, c2c=0 0 -1] +\DDDview[Fish Eye at Centre][fov=120] + +% We want to use all the defined views, as well as autoactivate the annotation. +% This means that the preview mostly sets dimensions, because it will not be +% shown much. (Only in viewers that don't support 3D Rich Media -- that is, all +% except Acrobat). +\RM[dice.u3d][ + views={Front, Back, Left, Right, Top, Bottom, Fish Eye at Centre}, + activation=auto, + scripts=3dmenu, +]{\textcolor{red}{\vrule height.5\textwidth width.5\textwidth}} + +% A few buttons for switching views. +\hlink[goto3dview::/N]{\frame{Next view}} +\hlink[goto3dview::(Back)]{\frame{View ``Back''}} +\hlink[goto3dview::5]{\frame{6th view in the list}} + +\clearpage + + +% Last media9 example ported. It has its own JavaScript file for animation, +% which can be customized at runtime using JavaScript actions that access 3D +% context of the annotation. +\filedef/e[malte]{malte.u3d} +\DDDview[malte][c2c=1 1 1, roo=0.25] +\RM[malte][ + activation=auto, + scripts={3dmenu, malte.js}, +]{\textcolor{blue}{\vrule height 0.5\textwidth width 0.5\textwidth}} + +% Normal JavaScript actions, but we access functions defined globaly in the 3D +% JavaScript context of "malte" Rich Media annotation. +\hlink[js:\DDDcontext{malte}.cntrClockWise();] {\includegraphics[height=1.44em]{boutona.pdf}} +\hlink[js:\DDDcontext{malte}.pause();] {\includegraphics[height=1.44em]{boutonb.pdf}} +\hlink[js:\DDDcontext{malte}.clockWise();] {\includegraphics[height=1.44em]{boutonc.pdf}} +\hlink[js:\DDDcontext{malte}.scaleSpeed(1/1.1);]{\includegraphics[height=1.44em]{boutond.pdf}} +\hlink[js:\DDDcontext{malte}.origSpeed();] {\includegraphics[height=1.44em]{boutone.pdf}} +\hlink[js:\DDDcontext{malte}.scaleSpeed(1.1);] {\includegraphics[height=1.44em]{boutonf.pdf}} + + +\end{document} diff --git a/macros/luatex/generic/pdfextra/examples/pdfextra-example-part.prc b/macros/luatex/generic/pdfextra/examples/pdfextra-example-part.prc Binary files differnew file mode 100644 index 0000000000..812479dcbb --- /dev/null +++ b/macros/luatex/generic/pdfextra/examples/pdfextra-example-part.prc diff --git a/macros/luatex/generic/pdfextra/examples/pdfextra-example.pdf b/macros/luatex/generic/pdfextra/examples/pdfextra-example.pdf Binary files differnew file mode 100644 index 0000000000..24ca714a75 --- /dev/null +++ b/macros/luatex/generic/pdfextra/examples/pdfextra-example.pdf diff --git a/macros/luatex/generic/pdfextra/examples/pdfextra-example.tex b/macros/luatex/generic/pdfextra/examples/pdfextra-example.tex new file mode 100644 index 0000000000..6704ac4b98 --- /dev/null +++ b/macros/luatex/generic/pdfextra/examples/pdfextra-example.tex @@ -0,0 +1,345 @@ +% PDFextra example file. +% This file, apart from other topics, shows how to port 3D examples from media9 +% (https://ctan.org/pkg/media9) documentation. You probably have to adjust path +% searching to succesfully compile this example, e.g.: +% +% \begtt +% env TEXMFDOTDIR=.:~/src/texlive/texmf-dist/source/latex/media9/files/ optex pdfextra-example.tex +% \endtt + +% Basic \OpTeX/ settings: +\enlang\enquotes +\fontfam[lm] + +% Load the package from standard search path: +\load[pdfextra] + +% Before using \hlink we must activate hyperlinks: +\hyperlinks\Blue\Green + +% Maybe we want to greet the user and jump to page 2 right at the start? +\openaction[ + js:{% + app.alert("Javascript alert, open action"); + console.println("printing to console from openaction");}, + pg:2, +] + + + +% Using document level JavaScript we can run initialization code right at the +% start (this is more flexible than \openaction). +\dljavascript[random]{ + function getRandomNumber() { + return 4; % chosen by fair dice roll, ![https://xkcd.com/221/] + } + console.println("initialized with seed " + getRandomNumber()); +} + + + +% For external references we need to `\filedef` "external" and "url" files +% beforehand: +\filedef/u[doc-internet]{http://petr.olsak.net/ftp/olsak/optex/optex-doc.pdf} +\filedef/x[doc-local]{optex-doc.pdf} + +% We then try to reference them using external references: +\hlink[extref:doc-internet:ref:langphrases]{OpTeX documentation, section \"Multilingual phrases and quotation marks".} +\hlink[extpgref:doc-internet:12]{OpTeX documentation, page 12.} + + + +% Insert a video twice with different settings: +\filedef/e[video]{example-movie.mp4} +% Insert video into page using Renditions mechanism with controls and autoplay. +\render[video][ + controls=true, + aactions=\renditionautoplay, +]{\picwidth=\hsize \inspic{example-image.pdf}} + +% We set red text color, blue border (RGB triplet) for "rendition" links. +\let\_renditionlinkcolor\Red +\def\_renditionborder{0 1 0} +% Then we add some buttons. +\hlink[rendition::play]{play} \hlink[rendition::pause]{pause} +\hlink[rendition::stop]{stop} \hlink[rendition::resume]{resume} + +% Render the same file again, but with different dimensions, no controls +% and explicit activation. +\render[video]{\inspic{example-image.pdf}} +% This one can't have buttons, because it doesn't have its own name -- "video" +% is used by the previous one. + + + +% Add already embedded video to the list of attachments shown in user +% interface. +\attach[video] + + +% We are done for this page, who not go to the next one? +% +% \hlink typesets in group, so we locally disable lining links (our button is +% bigger than normal line) +\hlink[named:NextPage]{\nolininglinks\typoscale[2000/2000]\inoval{NEXT PAGE}} + + + +\vfil\break + + + +% Deliver fresh random number to everyone who sees this page using "page open" +% ("O") trigger event of page's actions. +\pageactions[ + {O} {js:{app.alert("Random number for you is " + getRandomNumber());}} +] + +% Link to OpTeX's page reference ("pg:1") with "Split" transition: +\hlink[pg:1, transition:Split]{Go to previous page (with transition).} + + + +\vfil\break + +% A 3D Rich Media annotation with a custom view and script follows: + +% First we define the 3D file, so we can use same name for the view and have it +% auto included. +\filedef/e[example]{pdfextra-example-part.prc} + +% Then a simplistic "front" view. +\DDDview[example][ + projection=ortho, + roo=400, +] + +% A 3D JavaScript script that will define function to use with action button. +% (we write the script only now, so we don't have to carry around an extra file +% in the package, you would normally have the contents in the file -- "turn.js" +% in this case). +\newwrite\js +\immediate\openout\js=turn.js +% rotate every node by angle degrees along $x$ axis +\immediate\write\js{\detokenize{ +function turn(angle) { + angle = angle * Math.PI / 180; + for (var i = 0; i < scene.nodes.count; i++) { + var node = scene.nodes.getByIndex(i); + node.transform.rotateAboutXInPlace(angle); + } +} +}} +\closeout\js + +% The annotation itself, with our script above. +\RM[example][ + activation=auto, + scripts=turn.js, +]{\White\vrule width\hsize height\hsize} + +\hlink[js:\DDDcontext{example}.turn(10);]{Turn by 10 degrees along $x$ axis.}\par +\hlink[js:\DDDcontext{example}.turn(30);]{Turn by 30 degrees along $x$ axis.}\par +\hlink[js:\DDDcontext{example}.turn(90);]{Turn by 90 degrees along $x$ axis.} + + + +\vfil\break + + +% Here is the "epix" 3D model example from media9 documentation ported to +% pdfextra. +% +% First we define a view with the same name as the future Rich Media +% annotation. This view will then automatically be used if no `views` key is +% specified. No need to be excessive with decimal numbers. +\DDDview[epix][ + FOV=30, + c2c=4 2 3, + coo=4.41 2.19 0, + roo=430, +] + +% 3Dmenu is used often, lets use shortcut instead. +\filedef/e[3dmenu]{3Dmenu.js} +% We want to use the nice "epix" name for the view and annotation. +\filedef/e[epix]{epix.prc} + +% Just to spice things up, we diverge a bit from media9 and define custom +% background color. +\colordef\GreyBG{0.1\Black} + +% Define the 3D Rich Media annotation. One view is automatically detected from +% name ("epix"). Scripts will run in specified order at initialization time +% (when the annotation is activated). +\RM[epix][ + scripts={ + asylabels.js, + 3Dspintool.js, + 3dmenu, + }, + background=\GreyBG, +]{\picwidth=0.8\hsize \picheight=0.8\hsize \inspic{epixposter.pdf}} + + + +\vfil\break + + + +% Second media9 example is "dice" it has a lot of views, but they are simple. +% This time we can't use the view name deduction from `\RM` name -- there is +% more than one view. +\DDDview[Front][roo=27] +\DDDview[Back][roo=27, c2c=0 1 0] +\DDDview[Left][roo=27, c2c=-1 0 0] +\DDDview[Right][roo=27, c2c=1 0 0] +\DDDview[Top][roo=27, c2c=0 0 1] +\DDDview[Bottom][roo=27, c2c=0 0 -1] +\DDDview[Fish Eye at Centre][fov=120] + +% We want to use all the defined views, as well as autoactivate the annotation. +% This means that the preview mostly sets dimensions, because it will not be +% shown much. (Only in viewers that don't support 3D Rich Media -- that is, all +% except Acrobat). +\RM[dice.u3d][ + views={Front, Back, Left, Right, Top, Bottom, Fish Eye at Centre}, + activation=auto, + scripts=3dmenu, +]{\Red \vrule height.5\hsize width.5\hsize} + +% A few buttons for switching views. +\hlink[goto3dview::/N]{\frame{Next view}} +\hlink[goto3dview::(Back)]{\frame{View \"Back"}} +\hlink[goto3dview::5]{\frame{6th view in the list}} + +\vfil\break + + +% Last media9 example ported. It has its own JavaScript file for animation, +% which can be customized at runtime using JavaScript actions that access 3D +% context of the annotation. +\filedef/e[malte]{malte.u3d} +\DDDview[malte][c2c=1 1 1, roo=0.25] +\RM[malte][ + activation=auto, + scripts={3dmenu, malte.js}, +]{\Blue \vrule height.5\hsize width0.5\hsize} + +{\picheight=1.44em +% Normal JavaScript actions, but we access functions defined globaly in the 3D +% JavaScript context of "malte" Rich Media annotation. +\hlink[js:\DDDcontext{malte}.cntrClockWise();]{\inspic{boutona.pdf}} +\hlink[js:\DDDcontext{malte}.pause();]{\inspic{boutonb.pdf}} +\hlink[js:\DDDcontext{malte}.clockWise();]{\inspic{boutonc.pdf}} +\hlink[js:\DDDcontext{malte}.scaleSpeed(1/1.1);]{\inspic{boutond.pdf}} +\hlink[js:\DDDcontext{malte}.origSpeed();]{\inspic{boutone.pdf}} +\hlink[js:\DDDcontext{malte}.scaleSpeed(1.1);]{\inspic{boutonf.pdf}} +} + + +\vfil\break + + +% Lastly a showcase of transitions / page durations in \OpTeX/ slideshow. +% Because transitions are displayed only in fullscreen mode, we could request +% it by using `\fullscreen`. But we have more pages before this, so we didn't. + +% We just turn the rest of the document into slideshow. +\slides +\slideshow +% To see the transitions better, we color each page with a background color. +\global\pgbackground={\bgcolor \hrule height\pdfpageheight width\pdfpagewidth} +\glet\bgcolor=\Yellow + +% Transitions on all pages where it is not said otherwise will be of type +% "Wipe" and be 1 second long. +\transitions[Wipe:1] + +% The slides will by default be shown for 3 seconds before advancing to the +% next one. +\defaultpageduration[3] +% But the first slide has override to only be shown for 1 second. +\pageduration[1] + +\tit Presentation + +\subtit Transistions and durations + +\pg; +\glet\bgcolor=\Red +% All transitions are defined on the page object and they are shown when we go +% to that page. That means that we probably didn't see the "Wipe:1" animation +% of the first slide, because we already were there. However, this second slide +% with "Fade" is now shown correctly. (The first one also will, when we go +% back to it.) +\transition[Fade] + +\sec First slide + +Some\pg+ +% In a slideshow, by using \pg+ we are on a new PDF page with independently set +% transition/duration. We can use this to disable page durations -- we have +% seen the "single page override" in action on previous page, and the default +% duration on this "page". +\defaultpageduration[] + +% Also apply a long transition on this "page"/"slide"/"subslide". +\transition[Dissolve:4] + +% More uses of currently set default values follow. +text\pg+ + +to\pg+ + +test\pg+ + +slideshow + +\pg; +% Now show different transitions on differently colored pages: +\glet\bgcolor=\Green +\transition[Split:3:/Dm /V /M /I] + +\sec Second slide + +\transitions[Blinds:2] + +more\pg+ + +text + +\transitions[Wipe] + +\pg; +\glet\bgcolor=\Brown +\transition[Dissolve] + +\sec Third slide + +\pg; +\glet\bgcolor=\Red +\transition[Box] + +\sec More + +\pg; +\glet\bgcolor=\Yellow + +\sec slides + +\pg; +\glet\bgcolor=\Red + +\sec using + +\pg; +\glet\bgcolor=\Yellow + +\sec default transition + +bye\pg+ + +bye + +\bye diff --git a/macros/luatex/generic/pdfextra/pdfextra-doc.pdf b/macros/luatex/generic/pdfextra/pdfextra-doc.pdf Binary files differnew file mode 100644 index 0000000000..9534e46bfa --- /dev/null +++ b/macros/luatex/generic/pdfextra/pdfextra-doc.pdf diff --git a/macros/luatex/generic/pdfextra/pdfextra-doc.tex b/macros/luatex/generic/pdfextra/pdfextra-doc.tex new file mode 100644 index 0000000000..292bb34ab2 --- /dev/null +++ b/macros/luatex/generic/pdfextra/pdfextra-doc.tex @@ -0,0 +1,772 @@ +% vim: tw=80 +\load [doc, pdfextra] +\catcode`\.=11 + +% table notes (http://petr.olsak.net/optex/optex-tricks.html#tnote) +\newcount\tnotenum +\def\tnotelist{} +\def\tnote#1{\incr\tnotenum $^{\rm\_romannumeral\tnotenum}$\global\addto\tnotelist{{#1}}} +\def\tnoteprint{\typoscale[920/920]\par \tnotenum=0 + \ea\foreach\tnotelist + \do{\advance\tnotenum by1 \par $^{\rm\_romannumeral\tnotenum}$##1 }\par + \global\tnotenum=0 \gdef\tnotelist{}% +} + +\let\_cslinkcolor\Blue + +% allow hyperlinking of \OpTeX's control sequences (see doc.opm) +\let\_pdfextra_extdocaction\_pdfextra_urlaction +\let\_extdoclinkcolor\Blue +\def\_Xindex#1#2{\sdef{,#1}{}\slet{el:#1}{optexdoclink}} +\def\optexdoclink{\hlink[extdoc:\optexdocurl\#cs:\_tmpa]{\csstring\\\_tmpb}} +\def\optexdocurl{http://petr.olsak.net/ftp/olsak/optex/optex-doc.pdf} +\isfile{optex-doc.eref}\iftrue \input{optex-doc.eref}\fi + +% allow hyperlinks to TeX's control sequences (TeX in a Nutshell) +\def\Xeref#1{\sdef{,#1}{}\slet{el:#1}{texdoclink}} +\def\texdoclink{\hlink[extdoc:\texdocurl\#cs:\_tmpa]{\csstring\\\_tmpb}} +\def\texdocurl{http://petr.olsak.net/ftp/olsak/optex/tex-nutshell.pdf} +\isfile{tex-nutshell.eref}\iftrue \input{tex-nutshell.eref}\fi + + +\insertoutline{PDF extra} +\tit PDF extra -- extra PDF features for \OpTeX/ + +\hfill Version \_pdfextra_version + +\centerline{\it Michal Vlasák, 2021} +\bigskip + +\noindent +`PDFextra` is a third party package for \OpTeX/. It aims to provide access to +more advanced PDF features, which are currently not supported in \OpTeX/ -- +especially interactive and multimedia features. The development is hosted at +\url{https://github.com/vlasakm/pdfextra}. + +In the spirit of \OpTeX/ you may use these macros in any form you want. Either +by installing this package and doing `\load[pdfextra]` in \OpTeX/, or just by +copying some useful parts of this package into your documents / packages. +\OpTeX/ namespacing is used, but it can be easily stripped, if you wish to +incorporate these macros into other macro packages. The code currently depends +on \LuaTeX/, but mostly uses only pdf\TeX/ primitives and a few simple macros +from \OpTeX/. Additionally, the package can be used in plain LuaTeX and +LuaLaTeX (in a limited form), see section~\ref[user:plain+latex]. + +User documentation (`pdfextra-doc.tex`) and technical documentation interleaved +with source code (`pdfextra.opm`) are all typeset in this PDF file. Some +examples of usage are in the user documentation, but file +`pdfextra-example.tex` contains more examples. + +\insertoutline{Contents}\outlines{0} +\notoc\nonum \sec Contents +\maketoc + +\vfil\break + +\chap User documentation + +\sec Defining files + +Many commands provided by this package require you to supply a file <name>. This +is because many commands either work directly (like inserting attachments or +multimedia) or can optionally use files (like inserting JavaScript). +The \"right" way to use <name> is to first define the <name> with:\nl\indent +\^`\filedef``/<type>[<name>]{<path or URL>}`. + +Where <name> is the name you will use to refer to this file. It is currently +limited to ASCII only (as all \"<name>s" required by this package). +Interpretation of <path or URL> +depends on the type, which may be: + +\begitems + * `e`, \"embedded file". The file with path <path> will be embedded to the PDF + file. A file that is embedded this once, can later be used many times in + different contexts, e.g. you may use it to attach a video as an attachment but + also have it play on page 1 and even other pages. This is the best way, because + the resulting PDF file is self contained. + * `x`, \"external file". <path> can only be a path to the current directory. + To refer to the file only <path> is used, sort of like a reference. This way + the file you want to refer to {\em has} to be present in the same directory as + the PDF file when it is {\em viewed}! + * `u`, \"URL file". <URL> is the URL of the file you want to refer to. +\enditems + +All these create the same type of object, which ideally could be used +interchangibly everywhere a {\em file specification} is required in PDF. This is +sadly not always true. The limitations to only certain types of `\filedef`'s +will be mentioned in due sections. But as a rule of thumb, most of the time you +want to embed the files into PDF. The external/URL references are good for +refering to external files, although other methods are also possible there. + +Because most of the times you want <name> to be embedded file, you may omit the +prior definition and instead use the <path> itself. The file will be +autoembedded. + +Examples: + +\begtt +\filedef/u[doc-internet]{http://petr.olsak.net/ftp/olsak/optex/optex-doc.pdf} +\filedef/x[doc-local]{optex-doc.pdf} +\filedef/e[doc-embedded]{optex-doc.pdf} +\endtt + +\label[user:multimedia] +\sec Multimedia + +It is possible to insert video, audio and 3D files for playback/display inside +a PDF file (on a page). There are several different PDF mechanisms for +inserting multimedia. For audio/video this package uses the so called +\"Renditions", which currently have the best support in PDF viewers (fully +works in Acrobat and Foxit, partly in Evince and Okular). Rich Media +annotations are used for 3D art (works only in Acrobat Reader), although it is +possible to also insert audio/video using this mechanism it is very restricting +and Renditions are recommended. + +Use command \^`\render``[<name>][<optional key-value parameters>]{<appearance>}` for +inserting audio/video with Renditions or +\^`\RM``[<name>][<optional key-value parameters>]{<appearance>}` for inserting +audio/video/3D as Rich Media annnotation. +The result of this command is similiar to what `\inspic` +produces\fnote{But because annotations are involved, transformations using PDF +literals will not work as expected.}. Both commands expect <name> to be +\~`\filedef`'d name of the file to play/display. As usual, fallback for +interpreting <name> as path (and embedding it) is in place. It is recommended +to only use embedded files with both mechanisms (Rich Media requires it, +Renditions with other than embedded files do not work in Acrobat). Optional +key-value parameters may be used to customize default values. They may be left +out enitrely (including brackets). Last parameter, <appearance>, defines so +called \"normal apperance", which is shown before the annotation is activated +(audio/video starts playing or 3D scene is displayed). The dimensions of +resulting multimedia annotation will be taken from <appearance>. Most likely +you want to use a \"poster" picture (inserted with `\inspic`) as appearance -- +the dimensions will be taken from it and e.g. aspect ratio will be nicely +preserved. + +Customization of Renditions is possible using key-value parameters, but beware +that it mostly doesn't work in Evince and Okular (Acrobat and Foxit are fine in +this regard). The available parameters are in Table~\ref[tab:kv-renditions]. +Most customizations of Rich Media concern 3D art. Available parameters are +listed in Table~\ref[tab:kv-rm]. + +\midinsert +\label[tab:kv-renditions] +\caption/t Key value parameters available for Renditions (\~`\render`) +\cskip +\centerline{\table{lllp{72mm\fL}}{ +Key & Possible values & Default & Description \crll +`controls` & `true` or `false` & `false` & Whether to display audio/video player controls. \crl +`volume` & decimal betwen 0 and 100 & 100 & Audio volume. \crl +`repeat` & integer $\geq$ 0 & 1 & Number of repetitions (0 means loop forever). \crl +`background` & \OpTeX/ color & `\White` & Color used for part of the annotation not covered by video player (for wrong aspect ratios). \crl +`opacity` & decimal between 0 and 1 & 1 & Opacity of `background`. \crl +`aactions` & `\renditionautoplay` & (none) & Can be used for autoplay on page open. \crl +`name` & ascii string & `<name>` & Name for use with actions and scripts. \crll +}} +\endinsert + +\midinsert +\label[tab:kv-rm] +\caption/t Key value parameters available for Rich Media (\~`\RM`) +\cskip +\centerline{\table{lp{31mm\fL}lp{75mm\fL}}{ +Key & Possible values & Default & Description \crll +`activation` & `explicit` or `auto` & `explicit` & Whether to automatically activate the annotation on page open or display normal apperance until user clicks. \crl +`deactivation` & `explicit` or `auto` & `explicit` & Whether to automatically deactivate the annotation on page close or require explicit deactivation by user (from right click menu). \crl +`toolbar` & `true` or `false` & `true` & Whether to show 3D toolbar (with view and other options). \crl +`views` & comma separated list of view <name>s & `<name>` & List of names of 3D views to be used. The default is to try a view of same <name>. Beware that unknown views are silently ignored. \crl +`scripts` & comma separated list of script <name>s & (none) & List of names of JavaScript script file <name>s to be used. \crl +`name` & ascii string & `<name>` & Name for use with actions and scripts. \crll +}} +\endinsert + +The weird `name` key is only required if one media file is used +more than once and control using actions or JavaScript scripts is needed. + +Examples of video insertion: + +\begtt +% embed file under name "video" +\filedef/e[video]{example-movie.mp4} +% insert video into page using Renditions mechanism with controls and autoplay +\render[video][ + name=bigvideo, + controls=true, + aactions=\renditionautoplay, +]{\picwidth=\hsize \inspic{example-image.pdf}} + +% render the same file again, but with different dimensions, no controls +% and explicit activation +\render[video]{\inspic{example-image.pdf}} +\endtt + +When displaying 3D there are more things involved. First, only U3D and PRC can +be included in PDF files. The simplest way to show the 3D scene on page is without +any optional parameters: +\par\nobreak\begtt +\RM[part.prc]{\picwidth=\hsize \inspic{part.png}} +\endtt + +The resulting view will be what is defined in the 3D file. But it is possible +to customize it, by creating custom view. Or even more of them -- they will be +available in the user interface for easy switching, first one is considered +default. Parameters not defined in a custom view often take what is in the 3D +file as default value. \^`\DDDview``[<view name>][<key-value parameters>]` is +the command for defining 3D views. The brackets surrounding key-value +parameters have to be included even if no key-value parameters are used. The +available parameters are explained in Table~\ref[tab:kv-3dview]. + +\midinsert +\label[tab:kv-3dview] +\caption/t Key value parameters available for 3D views (\~`\DDDview`) +\cskip +\centerline{\table{lp{38mm\fL}lp{55mm\fL}}{ +Key & Possible values & Default & Description \crll +`projection` & `perspective` or `ortho` & `perspective` & Projection type to use (perspective distorts the view, e.g. parallel lines are not shown as such, but is more natural to human eye, orthogonal projection is what is generally used with technical parts). \crl +`scale` & decimal number & 1 & Scaling to use when orthogonal projection is used. \crl +`FOV` & number between 0 and 180 & 30 & Field of view for use with perspective projection. \crl +`background` & \OpTeX/ color & `\White` & Color used as background in the 3D scene. \crl +`rendermode` & {\typosize[9/10]`Solid`,\hfil\break`SolidWireframe`,\hfil\break`Transparent`,\hfil\break`TransparentWireframe`,\hfil\break`BoundingBox`,\hfil\break`TransparentBoundingBox`,\hfil\break`Wireframe`,\hfil\break`ShadedWireframe`,\hfil\break`Vertices`,\hfil\break`ShadedVertices`,\hfil\break`Illustration`,\hfil\break`SolidOutline`,\hfil\break`ShadedIllustration`} & (taken from 3D file) & Used rendering mode. See PDF standard\tnote{\url{https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf\#G12.2358303}} for more details. \crl +`lighting` & `White`, `Day`, `Night`, `Hard`, `Primary`, `Blue`, `Red`, `Cube`, `CAD`, `Headlamp` & (taken from 3D file) & Used lighting scheme. See PDF standard\tnote{\url{https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf\#G12.2358356}} for more details.\crl +`method` & `media9`, `manual`, `u3d` & `media9` & Method used for defining 3D camera position and orientation. \crll +}} +\tnoteprint +\endinsert + +The value of `method` key influences what other key-value +parameters are available. For details about the manual method see the technical +documentation~(\ref[mm-3dviews]). The `u3d` method works only with U3D files +(not with PRC files) and requires you to know the internal \"path" of the view +contained in the file and is hence not always useful (especially when the paths +are weirdly constructed by exporting applications). But if you know the path +you can use \"`method=u3d, u3dpath=<path>`". All Unicode characters are allowed in +<path>. + +The most useful method of specifying the camera position/orientation is +`method=media9`. The same method is also used in a number of other packages +(movie15, rmannot, Con\TeX/t). Its input are key-value parameters, listed in +Table~\ref[tab:kv-media9]. For illustration of the parameters check the +documentation\fnote{\url{https://mirrors.ctan.org/macros/latex/contrib/media9/doc/media9.pdf\#figure.8}} +of media9. + +\midinsert +\label[tab:kv-media9] +\caption/t Key value parameters available for media9 method of 3D views (\^`\DDDview`) +\cskip +\centerline{\table{lp{38mm\fL}lp{50mm\fL}}{ +Key & Possible values & Default & Description \crll +`coo` & three space separated (decimal) numbers & `0 0 0` & \"Center of orbit". Coordinates of the point camera is supposed to look at. \crl +`roo` & (decimal) number & `0` & Distance of camera from `coo`. \crl +`c2c` & three space separated (decimal) numbers & `0 -1 0` & \"Center of orbit to camera" vector. A directional vector (i.e. length doesn't matter). The direction the camera will be looking at is opposite of this vector. Default is view towards positive $y$.\crll +}} +\endinsert + +You can construct simple views only by using these few parameters. When talking +about meanings/names of different views it is needed to know the real placement +of the 3D object in the 3D world. But for a reasonably orientated object in the +center (`coo=0 0 0`) constructing simple views is very easy (keep in mind +`method=media9` is default): + +\begtt +\DDDview[front][ + projection=ortho, + roo=400, +] +\DDDview[left][ + projection=ortho, + roo=400, + c2c=-1 0 0, +] +\DDDview[top][ + projection=ortho, + roo=400, + c2c=0 0 1, +] +\DDDview[isometric][ + projection=ortho, + roo=500, + c2c=-1 -1 1, +] +\endtt + +We can then perhaps use these views in \^`\RM`: + +\begtt +% Auto activated 3D Rich Media annotation. White background just ensures +% the right dimensions. Comma in `views` is escaped using "{}". +\RM[part.prc][ + activation=auto, + views={front, left}, +]{{\White\vrule width\hsize height\vsize}} + +\RM[part.prc][ % the same 3D file, now with different views + name=part2, + activation=auto, + views={top, isometric}, +]{{\White\vrule width\hsize height\vsize}} +\endtt + +If you want to deduce the view parameters automatically it is possible. You can +just not specify any view, but include the `3Dmenu.js` script from media9 +package. It enables you to right click the annotation and select \"Get Current +View" (or even \"Generate Default View" which finds the view all by itself). A +window with generated parameters will show up. You can then copy the ones this +package understands (`c2c`, `coo`, `roo`) and use them. You don't have to be +excessive with precision, because after calculation everything gets rounded to +6 decimal places anyways. + +\begtt +% using 3Dmenu.js to generate 3D view parameters automatically +\RM[part.prc][ + name=part3, + activation=auto, + scripts=3Dmenu.js, +]{{\White\vrule width\hsize height\vsize}} +\endtt + +The use of `scripts` isn't limited to this though, there are many other +possibilities. First, you can use as many scripts as you want +(`scripts={script1.js, script2.js, ...}`), but be careful that 3D JavaScript is +kind of special, and has to come from embedded files (here we +were using the auto-embedding, but we could have used e.g. +\~`\filedef/e[3dmenu]{3Dmenu.js}` and then \"`3dmenu`" instead). For creating +your own scripts check out the Acrobat 3D JavaScript +API\fnote{\url{https://wwwimages.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/AcrobatDC_js_3d_api_reference.pdf}}. +It is possible to do different transformations and achieve animations using +\"time events". The implicit \"context" of 3D scripts can be accessed from +normal JavaScript actions (see~\ref[user:actions-3djs]). Vice versa 3D scripts +may access the global JavaScript environment using `host` object. + +More examples of 3D Rich Media, including usage of 3D JavaScript, are available +in the example file `pdfextra-example.tex`. They show how it is possible to +port the examples used by media9. + +\sec Actions + +Actions are very important aspect of interactivity in the context of PDF. There +are a few very useful types of actions, like \"goto" actions which jump to +other part of document. There are also a few ways how to {\em execute} actions. +The most usual is a clickable area on page, but clickable bookmarks (\"document +outline") also execute actions behind the scenes. \OpTeX/ supports only basic +\"goto" and \"URI" actions using `\ilink` (used for `\ref`, `\cite`, etc.) and +`\ulink` (used for `\url`). + +This package offers generalization of this mechanism. The core of it is a way +of specifying an action. This syntax is called <action spec> and is used for +example by `\hlink` command, which can replace both `\ilink` and `\ulink`. +<action spec> is a comma separated list of `<type>:<arguments>`. where <type> +refers to the action type and the syntax of arguments is dependant on <type>. +Leading spaces are ignored, trailing aren't. You probably won't often use the +possiblity of specifying multiple actions, but it is for chaining execution of +several actions. + +\^`\pdfaction``[<action spec>]` is available for lower level creation of +different actions, but for clickable areas on page you will use +\^`\hlink``[<action spec>]{<text>}` with a very similiar syntax. Although note, +that `\hlink`'s interface is also not really high level and wrapping it inside +macros like `\ref` or `\url` might be beneficial. <text> will be typeset +directly and the area it occupies will be clickable. Clicking it executes +action defined by <action spec>. Line breaks inside <text> will be possible, in +that case several clickable rectangles will be created, one for each line. +Normally in text you want the areas to be of the same height and depth +(calculated from `\baselineskip`), to achieve sort of a lining, uniform effect. +If you want to define big clickable buttons, you may need to turn off the +lining effect using \^`\nolininglinks`. It respects groups, but a counterpart +(\^`\lininglinks`) is also available. + +There are a few predefined action types (<type>): `url`, `extref`, `extpgref`, +`named`, `transition`, `js`, `goto3dview` and `rendition`. They will be explained in a +moment. Any unrecognized <type> is understood as an \"internal link", where +`<type>:<link>` is the destination of the link. Hence it is possible to +use `\hlink` as `\ilink` for example with \OpTeX/'s normal types +of internal links. For example: +\begtt +See section~\hlink[ref:section]{2.2.13} or page~\hlink[pg:5]{5}. +\endtt +`\ulink` may be replaced like this: +\begtt +Visit CTAN's \hlink[url:https://www.ctan.org/]{website}. +\endtt + +Before we really get into different types of actions, there is a nicer command +for setting the initial (\"open") action of PDF document, which is executed +when the document is opened. It defaults to opening the page with the zoom +level according to viewing user's preferences. But you can change this using +\^`\openaction``[<action spec>]`: + +\begtt +\openaction[pg:2] +\endtt + +\secc External references +There are two actions analogous to internal links that can be used to link to +external documents. `[extref:<name>:<named destination>]` can be used to refer +to named locations in a PDF document prepared by `\filedef` with <name>. +`[extpgref:<name>:<page number>]` is similiar but refers to a page number. +Although it would be nice, these actions aren't well supported by all viewers. + +Example: + +\begtt +\hlink[extref:doc-internet:ref:langphrases]{OpTeX documentation, + section \"Multilingual phrases and quotation marks".} +\hlink[extpgref:doc-internet:12]{OpTeX documentation, page 12.} +\endtt + +(Note that \"`:`" in \"`ref:langphrase`" is not part of the syntactic rule, it +is just the value of <named destination> in this case.) + +A little bit of customization is possible, see~\ref[actions-jump]. + +Because of the poor support you may find luck with the less universal url action with `#fragment`: + +\begtt +\hlink[url:http://petr.olsak.net/ftp/olsak/optex/optex-doc.pdf#ref:langphrases] + {OpTeX documentation, section \"Multilingual phrases and quotation marks".} +\endtt + +\secc Named actions +There are four defined in PDF standard, but viewers may support more. All in +examples: + +\begtt +\hlink[named:NextPage]{Go to next page,} +\hlink[named:PrevPage]{go to previous page,} +\hlink[named:FirstPage]{go to first page,} +\hlink[named:LastPage]{go to last page.} +\endtt + +\secc Transition actions + +Transition actions generally make sense only when chained {\em after} jump +actions. The specified transition/animation will occur, before destination is +opened, but it will not override a transition defined for the particular page. +The syntax is `[transition:<transition spec>]`. See~\ref[user:transitions] for +more information about `<transition spec>`. + +Example: +\begtt +\hlink[ref:yellow-slide, transition:Box:3:/M /O] + {Go to yellow slide with long outward Box transition} +\endtt + +\secc JavaScript actions + +They allow executing pieces of JavaScript code using syntax: +`[js:<name or script>]`. If <name or script> is a validly `\filedef`'d the script from file +<name> will be executed (only embedded files are valid). Otherwise <script> will +be used directly. Apart from reuse in different documents, scripts loaded from +files may be encoded in UTF-16BE and hence support Unicode, inline <script>s +current don't. + +Examples: + +\begtt +\openaction[js:{% + app.alert("Javascript alert, open action"); + console.println("printing to console from openaction"); +}] + +\filedef/e[jstest]{test.js} +\hlink[js:jstest]{JavaScript action from file} +\endtt + +(Note how braces were used to guard the comma in JavaScript code from being +interpreted as a action separator.) + +In these actions you may want to use your own functions which should be defined +before user has a chance of activating any other JavaScript actions. This is the +purpose of \"document level" JavaScript actions. They function exactly the same +way as normal JavaScript actions, but have names (although meaningless, they +must be unique) and are executed in order of definition. Use +\^`\dljavascript``[<name>]{<name or script>}` for defining these actions: + +\begtt \adef![#1]{\url{#1}} +\filedef/e[preamble]{preamble.js} +\dljavascript[preamble]{preamble} + +\dljavascript[initialization]{% + var data = 42; + function getRandomNumber() { + return 4; // chosen by fair dice roll, ![https://xkcd.com/221/] + } + console.println("initialized with seed " + getRandomNumber()); +} +\endtt + +The JavaScript API available is not the same as the one in the web browsers. It +is instead specified by Adobe: +\url{https://wwwimages.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/js_api_reference.pdf}. + +\label[user:actions-3djs] +\secc 3D JavaScript actions + +There are no \"special" JavaScript actions for dealing with 3D. Normal +JavaScript actions are used. You just have to access the 3D context of the +annotation you want to control. For annotation with <name> the context is made +available in \^`\DDDcontext{<name>}`. Under this context object you find all +global definitions from the respective 3D scripts. For example if a \"`turn`" +function is defined, it is possible to call it like this: + +\begtt +\hlink[js:\DDDcontext{part3}.turn();]{Turn by 90 degrees along $x$ axis.} +\endtt + +\secc GoTo3Dview actions + +GoTo3Dview actions allow changing the view of the 3D scene to one of the +predefined views (those listed by `views` comma separated list of \~`\RM`). The +syntax is `[goto3dview:<name>:<view>]`. <name> is name of the annotation whose +view we want to change and <view> is the intended view. You can refer to last +inserted Rich Media annotation using empty name. For <view> you can either use +\"`(<view name>)`" (e.g. \"`(part)`"), index of the view in the view list (zero +based, e.g.\ \"`0`" for the first view) or one of the special values: \"`/N`" +(next), \"`/P`" (previous), \"`/F`" (first) \"`/L`" (\"last"). + +\begtt +% let's define an annotation with a few views +\RM[part.prc][ + activation=auto, + views={front, left, top, isometric}, +]{{\White\vrule width\hsize height\vsize}} + +% try the different methods of reffering to views +\hlink[goto3dview::/N]{Next view}, +\hlink[goto3dview::(left)]{left view} and +\hlink[goto3dview:part.prc:3]{third view}. +\endtt + +\secc Rendition actions + +Rendition actions can be used to control playback of \"Rendition annotations". +They use the syntax `[rendition:<name>:<operation>]`, where <name> refers to +the name of the rendition to control and <operation> is one of `play`, `stop`, +`pause` or `resume`. As a convenience, you can refer to last inserted rendition +using empty name. If a file has been \~`\render`ed more than once with the same +name, the action will influence the first instance. + +Beware that currently these actions do not work in Evince and Okular (but do in +Acrobat and Foxit). + +Examples: + +\begtt +% rendition with name=video, that we want to control +\render[video]{\inspic{example-image.pdf}} + +% we want the rendition action to have yellow border and red content +\def\_renditionborder{1 1 0} +\let\_renditionlinkcolor\Red + +To start playing the video, click \hlink[rendition::play]{\"Play"}. +After that you can \hlink[rendition:video]{pause}. +\endtt + +\label[user:transitions] +\sec Transitions and other page attributes + +In PDF there are a few settings that can be set as {\em page attributes}. This +means that they apply only to said page. For setting these page attributes, +there are two options: + +\begitems +* value for \"current page" (or rather the page where the command appears), +* default value used if \"current page" value is not set. +\enditems + +While this package contains mechanism to handle all page attributes, not that +many are useful for end user. The interesting ones remaining are: + +\begitems +* Transitions and page durations. When page has the transition attribute any +jump to this page will display the requested animation (customized by the +corresponding parameters). Page duration is the time before PDF viewer will auto +advance to the next page. \^`\transition``[<transition spec>]` sets transition +for the \"current page", \^`\transitions` sets the default. +`<transition spec>` has three parts:\nl\indent +`<animation type>:<duration>:<raw PDF attributes>`\nl +\noindent <animation type> is one of `Split`, `Blinds`, `Box`, `Wipe`, +`Dissolve`, `Glitter`, `Fly`, `Push`, `Cover`, `Uncover` and `Fade`, or the +special value `R` which essentially means no animation and instantaneous +transition (regardless of the set duration of transition). <duration> is the +duration of transition in seconds (integer or decimal number). +<raw PDF attributes> may be used to customize +the animations (for example `/M` can set the direction of motion of `Split`, +`Box` and `Fly`, e.g. `/M /I` for inward motion). For the raw PDF attributes +refer to the standard +itself\fnote{\url{https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf\#G11.2295795}}. +`:<raw PDF attributes>` or even `:<duration>:<raw PDF attributes>` may be omitted. +Default values specified by PDF standard will be used in that case (1 second +duration and default values for all attributes). Page durations can be set +either using \^`\defaultpageduration``[<duration>]` or +\^`\pageduration``[<duration>]`, where duration is in seconds (default is no auto +advancement, i.e. $\infty$). +Examples: + +\begtt +% unless stated otherwise all pages will have Wipe animation +% with 1 second duration +\transitions[Wipe:1] + +% but this page has a 1 second Fade animation +\transition[Fade] + +\pg+ % (if using \slides) + +% and this page has 3 second Split animation +% with vertical direction and inward motion +\transition[Split:3:/Dm /V /M /I] +\endtt + +Note that transitions are only displayed when in {\em full-screen mode}. You +can use \~`\fullscreen` to have the document automatically open in full-screen +mode. + +* Additional actions. It is possible to define actions which will respond to +page events: page open (`/O`) and page close (`/C`). They can be set using +\^`\defaultpageactions``[<additional actions spec>]` (the default for all pages) and +\^`\pageactions``[<additional actions spec>]` (current page override). +<additional actions spec> consists of braced pairs of \"event" (O or C in this +case) and <action spec>. Example: + +\begtt +\pageactions[ + {O} {js:{app.alert("Page open, random = " + getRandomNumber());}} + {C} {js:{app.alert("Page close!");}} +] +\endtt + +\enditems + +\sec Attachments + +Every file embedded into a PDF file may optionally be presented in the user +interface as an embedded file. This allows readers of the document to save or +open the file. + +PDF allows two ways of presenting attachments -- using annotations (the +attachments is represented by a rectangular icon on a page) or global array of +attachments (attachments are visible in PDF viewers toolbar). The first method +is intended more for document reviewers than for primary insertion. Hence this +package supports only the second type. + +You may add an embedded file to the global attachments array using +\^`\attach``[<name>]`. As usual, name is either a <name> defined with +`\filedef` or alternatively a path to file which will be embedded (and +`\filedef`d) automatically. It is not possible to `\attach` files referenced by +path or URL. + +If you want to automatically display toolbar with embedded files, consider +using `\showattached` (see section~\ref[user:document-view]). + +\label[user:document-view] +\sec Document view + +You can choose what is shown when document is opened with commands: + +\begitems +* \^`\fullscreen` (the document is opened in full-screen mode), +* \^`\showoutlines` (show bookmarks/outlines toolbar) +* \^`\showattached` (show attachments in toolbar) +\enditems + +The commands are mutually exclusive and only the first appearing one will be respected. + +You can request two page view (odd pages on the right) using +\^`\duplexdisplay`. It is useful for more natural display of double sided +documents. Because it may not be desirable to automatically apply this, it is +independent of `\margins`. + +\label[user:plain+latex] +\sec Usage in plain \LuaTeX/ or LuaLa\TeX/ + +You can use this package also from plain LuaTeX by adding to your document: + +\begtt +\input pdfextra +\endtt + +\noindent or for LuaLa\TeX/: + +\begtt +\usepackage{pdfextra} +\endtt + +See the file `pdfextra-example-latex.tex` for the adaptation of the \OpTeX/ +examples to \LaTeX/. + +The usage of the macros described in this document is the same, but there are +limitations: + +\begitems +* {\em Color}. Where this package expects \"\OpTeX/ color" key value argument +(e.g. `\Blue`), you have to use an RGB triplet instead (e.g. `0.0 0.0 0.0` or +the shorter `0 0 0`). + +But for text color setting, you can get away with wrapping commands from +La\TeX/'s `color` package, e.g. to customize link border/color: + + \begtt + \sdef{_renditionlinkcolor}{\color[red]} + \endtt + +* {\em Initialization}. In \OpTeX/ you normally have to initialize hyperlinks +with the command\nl +`\hyperlinks<color for internal links><color for external links>`.\nl +This is not required by this package. You can instead set the color by setting +`\_linkcolor` (fallback for all link types), `\_ilinkcolor` / `\_elinkcolor` +(internal / external links). E.g. if you have the \LaTeX/ `color` package +loaded, you can get blue links like this: + + \begtt + \sdef{_linkcolor}{\color[blue]} + \endtt + +In addition to the above this means all links will be blue except \"rendition" +links. + +* {\em Openaction}. If the package `hyperref` is used, then `\openaction` will +not work. + +* {\em Labels / hyperlink destinations}. \OpTeX/ uses very simple and consistent +scheme for labels / hyperlink destinations: + + \begitems + * `ref:<label>` -- result of `\label[<label>]` + * `toc:<tocrefnum>` -- result of `\chap`/`\sec`/`\secc` titles + * `pg:<gpageno>` -- created on each page with global numbering from 1 + * `cite:<bibpart>/<bibnum>` -- bibliography references, + * `fnt:<gfnotenum>` -- link form text to footnote + * `fnf:<gfnotenum>` -- link from footnote to text + * `url:<url>` -- used by `\url` or `\ulink`, + \enditems + + Hence these labels / destinations can be used with `\hlink`, e.g. to make text + `page 5` a link to page 5, one can use: + + \begtt + \hlink[pg:5]{page 5} + \endtt + + This is not possible in plain \LuaTeX/ (no destinations are created) or + LuaLa\TeX/ (different and incompatible destination names). You would have to + create your own destinations adhering to the naming convention + `<type>:<arguments>` to be able to use `\hlink` as intended for some links. + +\enditems + +\chap Technical documentation + +This is the technical documentation. It is intended for those who want to know +how this package works internally. Casual users shouldn't need to read this. But +if you would like to customize anything or perhaps just use some part of this +package, feel free to copy paste and use anything you want in \OpTeX/'s spirit. + +This documentation is interleaved within the source itself, both are contained +in a single file, `pdfextra.opm` (according to \OpTeX/ conventions). The user +documentation is instead contained in `pdfextra-doc.tex`, which itself +`\input`'s the documented source file `pdfextra.opm` so that the user and +technical documentation is available in a single PDF file, `pdfextra-doc.pdf`. + +\printdoc pdfextra.opm + +\bye diff --git a/macros/luatex/generic/pdfextra/pdfextra.opm b/macros/luatex/generic/pdfextra/pdfextra.opm new file mode 100644 index 0000000000..bccf096484 --- /dev/null +++ b/macros/luatex/generic/pdfextra/pdfextra.opm @@ -0,0 +1,1728 @@ +% vim: tw=80 +\_def\_pdfextra_version{0.2} +\_codedecl \RM {Extra PDF features (v\_pdfextra_version)} +\_namespace{pdfextra} + + \_doc + \sec Package initialization + + We ensure that hyperlinking is active. Our fallback \`\_linkcolor` must + exist. We also use it for `\hyperlinks` if the user didn't enable + `\hyperlinks` yet (we don't want to override user setting). + \_cod + +\_ifdefined\_ilinkcolor\_else + \_ifdefined\_linkcolor + \_ea\_let\_ea\_linkcolor \_ifdefined\Blue\Blue\_else\_empty\_fi + \_else + \_let\_linkcolor\linkcolor + \_fi +\_fi + +\_ifx\_dest\_destactive\_else + \_hyperlinks\_linkcolor\_linkcolor +\_fi + + \_doc + We are in the \OpTeX/ package namespace. A couple of shortcuts are defined + here: \`\.isdefined`, \`\.trycs`, \`\.cs` \`\.slet`, \`\.slet`, \`\.sdef` and + \`\.sxdef`. They all hard code the package name, because we already have too + many levels of indirection. + \_cod + +\_def\.isdefined#1{\_isdefined{_pdfextra_#1}} +\_def\.trycs#1{\_trycs{_pdfextra_#1}} +\_def\.cs#1{\_cs{_pdfextra_#1}} +\_def\.slet#1#2{\_slet{_pdfextra_#1}{_pdfextra_#2}} +\_def\.sdef#1{\_sdef{_pdfextra_#1}} +\_def\.sxdef#1{\_sxdef{_pdfextra_#1}} + + \_doc + \sec Helper macros + The macros here are just helpers for the macros to follow. They are not + useful generally, but proved useful in the expandable context of writing to + PDF files. + + Already the first one limits the use to \LuaTeX/ (but who needs other engines + anyways :). \`\.emptyor`<possibly empty text><text to use when first argument is + nonempty> checks whether the first argument is empty, if not it expands the + second argument which can use the text from the first argument with + \`\.nonempty`. \`\.attrorempty`<attribute name><value> builds upon the first + one and is really useful for PDF dictionaries, when we don't want to write an + attribute without a value (a default specified by standard will be used instead). + \_cod + +\_def\.emptyor#1#2{% + \_immediateassignment\_edef\.nonempty{#1}% + \_ifx\.nonempty\_empty\_else #2\_fi +} +\_def\.attrorempty#1#2{\.emptyor{#2}{/#1 \.nonempty}} + + \_doc + There is a dillema for handling colors. While typesetting it is possible to + use greyscale, CMYK or RGB colors. But there are contexts where it is + possible to only use RGB colors. We want to provide the user with two + possibilities of specifying colors: + + \begitems + * RGB color using PDF triplet (e.g. `1 0 0`), + * \OpTeX/ color using control sequence (e.g. `\Blue`) + \enditems + + Both are handled by \`\.colortorgbdef`<cs><color specification>, which + defines <cs> to the corresponding PDF RGB triplet. The indirection with + defining a macro is because we want to use the processed color within + expansion only contexts where grouping is not possible. + \_cod + +\_def\.colortorgbdef#1#2{\_bgroup + \_def\_setrgbcolor##1{##1}% + \_def\_setcmykcolor##1{\_cmyktorgb ##1 ;}% + \_def\_setgreycolor##1{##1 ##1 ##1}% + \_xdef#1{#2}% + \_egroup +} + + \_doc + \`\.xaddto``\macro`{<text>} is a natural extension of \OpTeX's `\addto` that + expands <text> and is global.\nl + \`\.tmp` is used throught the package for temporary values. + \_cod + +\_def\.xaddto#1#2{\_edef\.tmp{#2}% + \_global\_ea\_addto\_ea#1\_ea{\.tmp}% +} + + \_doc + This package defines a few commands in the form + `\macro[<name>][<optional arguments>]{<text>}`. To make it possible to omit + the `[<optional arguments>]` \`\.secondoptdef` is defined. + + `\.secondoptdef\<macro><parameters>{<body>}` + defines `\macro` with first mandatory argument in brackets (saved to + \`\.name`). Second optional argument in brackets is scanned using helper + macro defined with `\optdef` and is saved to `\_opt` token list). Additional + <parameters> can be specified as with `\optdef` (numbered from `#1`). + \_cod + +\_def\.secondoptdef#1{% + \_def#1[##1]{\_def\.name{##1}\.cs{sopt:\_string#1}}% + \_ea\_optdef\_csname _pdfextra_sopt:\_string#1\_endcsname[]% +} + + \_doc + When processing comma separated lists sometimes it is needed to ignore the + remaining text. For this we use \`\.untilend` macro which ignores everything + up to dummy \`\.end`. This is analogous to \OpTeX/'s `\_finbody` used for the + same purpose. Sometimes `\.end` is used as sentinel and compared in `\ifx` + tests, hence we define it to a unique value. + \_cod + +\_def\.untilend#1\.end{} +\_def\.end{_pdfextra_end} + + \_doc + For various uses it is necessary to know the number of page where something + happens. This has to be handled asynchronously with `\write`. Here we use + \OpTeX/ specific `.ref` file and associated macros, but this could be + replaced as long as the same interface is exposed. + + \`\.setpageof`<name> writes \`\.Xpageof`<name> to the `.ref` file. In the next + \TeX/ run `\.Xpageof` finds out the page number (`\gpageno`) from \OpTeX/'s + `\_currpage` and saves it so that \`\.pageof`<name> can retrieve it. In the + first run we can't be sure of the page where the content will end up. As a + rough estimate we take the current page~-- this actually works well for + slides where page breaks are manual. + + When `.ref` file is read along with the defintion of `\.Xpageof` this package + has not been loaded yet. Hence we can't use namespaced variants of + `\.isdefined`, etc. + \_cod + +\_refdecl{% + \_def\.Xpageof#1{\_isdefined{_pdfextra_pageof:#1}\_iffalse + \_sxdef{_pdfextra_pageof:#1}{\_ea\_ignoresecond\_currpage}\_fi + }% +} + +\_def\.setpageof#1{\_openref \_ewref\.Xpageof{{#1}}} + +\_def\.pageof#1{% + \.trycs{pageof:#1}{% + \_the\_numexpr\_gpageno+1\_relax % best effort = current page num + }% +} + + \_doc + \label[files] + \sec Handling of files + + Handling of files is a big topic of this package. Files are everywhere~-- + files containing multimedia, JavaScript script files, attachments, externally + referred files\dots Therefore a more sophisticated mechanism for handling + files is needed. The mechanism introduced in this section handles all three + cases of a {\em file specification}: + + \begitems + * files embedded in the PDF (\"e", embedded file), + * files determined by path (\"x", external file), + * files determined by URL (\"u", url file). + \enditems + + Although ideally all three would be interchangible this is not always the + case, because e.g. some media files must be embedded and linking to external + resources does not work with embedded files. + + In most cases there are two many names and other associated values involved: + \begitems + * Some kind of a \"friendly" name. This one is sometimes shown by PDF viewers. + * The real name of the file. Also shown but in different contexts. + * The path or URL used to determine the file. + * MIME type of the file. + \enditems + + For example when talking about \OpTeX/'s documentation we might have a + friendly name of \"opdoc", file name of \"`optex-doc.pdf`", URL of + \"\url{http://petr.olsak.net/ftp/olsak/optex/optex-doc.pdf}" and MIME type of + \"application/pdf". Different subset of them is required in different + contexts, but the user should only have to specify the friendly name (by + which they will refer to the file) and the path/URL of the file. The rest + will be deduced. The friendly name is used as a handle and {\em is usable} + in all places where file specification is required (although it may not + produce conforming output, see above). + + In this two step process~-- definition and (re)use~-- we introduce a command + for defining files: \`\filedef``/<type> [<friendly name>]{<path or URL>}`. The + macro itself does general definitions and dispatches the type dependant work + to other macros in the form `_filedef:<type>`. + \_cod + +\_def\.filedef/#1#2[#3]#4{% + \.sxdef{filename:#3}{(\.filename{#4})}% + \_edef\.tmp{\.exttomime{\.fileext{#4}}}% + \_ifx\.tmp\_empty + \_opwarning{MIME type of '#4' unknown, using '\.defaultmimetype'}% + \_edef\.tmp{\.defaultmimetype}% + \_fi + \.sxdef{filemime:#3}{\.tmp}% + \.cs{filedef:#1}{#3}{#4}% +} +\_nspublic \filedef ; + + \_doc + Types \"e", \"x", \"u" are predefined, anything else would essentialy be a + variant of these. + + External file (\"x") is determined only by path. + \_cod + +\.sdef{filedef:x}#1#2{% + \.slet{filespec:#1}{filename:#1}% +} + + \_doc + URL file (\"u") is determined by URL. Using all sorts of characters is + allowed by using `\_detokenize`. This time it is necessary to create full + {\em file specification}~-- a dictionary, where the \"file system" is URL. + \_cod + +\.sdef{filedef:u}#1#2{% + \.sdef{filespec:#1}{<</FS /URL /F (\_detokenize{#2})>>}% +} + + \_doc + Embedded files (\"e") are the most interesting ones. For further use (e.g. + for displaying the embedded files as attachments) MIME type is required. It + is saved in the stream as a `\Subtype`, encoded as a PDF name (e.g. + `/video#2Fmp4`). The embedded file stream must be wrapped in a full {\em file + specification}, which has the `/EF` (\"embedded file") entry. Also the + friendly name is used for some purpose by PDF viewers, so it set in `/Desc` + (description). + \_cod + +\.sdef{filedef:e}#1#2{% + \_edef\.tmp{\.cs{filemime:#1}}% + \_isfile{#2}\_iffalse + \_opwarning{file '#2' not found}% + \_fi + \_pdfobj stream + attr{/Type /EmbeddedFile /Subtype \_ea\.mimetoname\_ea[\.tmp]} + file {#2}% + \_pdfrefobj\_pdflastobj + \.sxdef{filestream:#1}{\_the\_pdflastobj\_space 0 R}% + \_pdfobj {<</Type /Filespec + /F \.cs{filename:#1} + /Desc (#1) + /EF << /F \_the\_pdflastobj \_space 0 R >>% + >>}% + \_pdfrefobj\_pdflastobj + \.sxdef{filespec:#1}{\_the\_pdflastobj\_space 0 R}% +} + + \_doc + Now the less interesting part~-- determining the file names from paths and + determining MIME types. The file name is the part after the last \"`/`" (if + any). The file extension is the part after last \"`.`" (if any). + \_cod + +\_def\.filename#1{\_ea\.filenameA#1/\.end} +\_def\.filenameA#1/#2{\_ifx\.end#2#1\_else\_afterfi{\.filenameA#2}\_fi} + +\_def\.fileext#1{\_ea\.fileextA#1.\.end} +\_def\.fileextA#1.#2{\_ifx\.end#2#1\_else\_afterfi{\.fileextA#2}\_fi} + + \_doc + MIME type is determined from file extension (e.g. `mp4` is \"video/mp4"). For + mapping of file extensions to MIME types we abuse \TeX/'s hash table which + gets populated with \"known MIME types". This necessarily means that the + database is incomplete. Users can define their own additional mappings, or + they can contribute generally useful ones to this package. + + The default MIME type (used for unknown file extensions) is + \"application/octet-stream"~-- binary data. + + The uninteresting MIME type database itself is at the very end (\ref[mime]). + \_cod + +\_def\.mimetoname[#1/#2]{/#1\_csstring\#2F#2} + +\_def\.defaultmimetype{application/octet-stream} +\_def\.exttomime#1{\.trycs{mimetype:#1}{}} + + \_doc + Here we define an \OpTeX/ style \"is-macro" that checks whether the file has + already been defined~-- \`\.isfiledefined``{<name>}\iftrue` (or `\iffalse`). + The case where the file has not + been defined using `\filedef` can be handled in a lot of ways. As a default + we interpret <name> as path and try to embed it. Because the path from <name> + is used as the \"friendly name" the file will be embedded only once even when + requested more times. + \_cod + +\_def\.isfiledefined#1#2{\.isdefined{filespec:#1}\_iftrue\_else + \_afterfi{\.fileundefined{#1}}\_fi#2% +} + +\_def\.fileundefined#1{\_isfile{#1}\_iftrue\.filedef/e[#1]{#1}\_else + \_opwarning{file '#1' not found, ignored}\_ea\_unless\_fi +} + +% strict requirement of preceeding `\filedef` can be set like this: +%\_def\.fileundefined#1{\_opwarning{file '#1' is not defined, ignored}\_unless} + + \_doc + \label[actions] + \sec PDF actions + + The core of interactivity in PDF are actions. They are all initialy handled + by \`\pdfaction``[<action spec>]`. <action spec> is a comma separated list of + `<type>:<arguments>`. Leading spaces in the elements of the list are ignored + using undelimited-delimited argument pair trick. + + An invocation could look like this: + + \begtt + \pdfaction[ + js:{app.alert("Yay JavaScript, going to page 5");}, + ilink:pg:5, + transition:Wipe, + ] + \endtt + + This is why we have to be very careful when loading the contents between `[]` + to arguments. In particular, we can't split immediatly using `[#1:#2]`, + because this would discard the braces guarding the comma in the JavaScript + code. However we also need to find out the {\em type} of action which is + taken as a type of the first action (`js` in this case). + \`\.pdfactiontype``[<action spec>]` does this~-- we don't mind that there the + braces are lost. + + `\pdfaction` processes the list, to create a chain of actions using `/Next` + field. The handling of each action type is up to macro + `\_pdfextra_<type>action`, which receives `[<type>:<arguments>]`. Because of + this a single type handler can handle multiple different actions, as is the + case with `\.ilinkaction` which is the fallback for unknown action types. + \_cod + +\_def\.pdfaction[#1#2]{\.pdfactionA#1#2,\.stop\.end} +\_def\.pdfactionA#1,#2#3\.end{% + <<% + \.pdfactionB[#1]% + % next action + \_ifx\.stop#3\_else\_space + /Next \_afterfi{\.pdfactionA#2#3\.end} % intentional space + \_fi + >> +} +\_def\.pdfactionB[#1:#2]{\.trycs{#1action}{\_ea\.ilinkaction}[#1:#2]} +\_nspublic \pdfaction ; + +\_def\.pdfactiontype[#1:#2]{#1} + + \_doc + \label[actions-additional] + \secc Additional actions + Some PDF objects, like pages and some annotations, can also have \"additional + actions". These are actions which will be executed when an event happens~-- + like page getting opened for `/O` action in page's additonal actions or `/PO` + in annotation's additional actions. For constructing these additional actions + we define a helper macro \`\.pdfaactions`. The use is as something follows: + \begtt \catcode`<=13 \adef|{\csstring<} + /AA || \.pdfaactions{ {O} {<action spec 1>} {C} {<action spec 2>} } >> + \endtt + To produce something this: + \begtt \catcode`<=13 \adef|{\csstring<} + /AA || /O ||<action 1>>> /C ||<action 2>>> >> + \endtt + \_cod + +\_def\.pdfaactions#1{<<\.pdfaactionsA #1\.end\.end>>} +\_def\.pdfaactionsA#1#2{\_ifx\.end#1\_else /#1 \_ea\.pdfaction\_ea[#2]\_ea\.pdfaactionsA\_fi} + + \_doc + \label[actions-link] + \secc Link annotations + The main use of actions~-- annotations of `/Subtype /Link`. Annotation of + this type creates an active rectangular area on the page that executes a PDF + action (or chain of them in the general case). + \`\hlink``[<action spec>]<text>` is macro that typesets <text> and makes area + occupied by it active according to <action spec>. All action types are + supported, the mechanism is completely generic. + + The `\pdfstartlink`/`\pdfendlink` primitives are used to denote the part of + the page where <text> appears as active. \LuaTeX/ will then handle even the + situations where <text> gets broken across multiple lines (by creating + multiple rectangular annotations to cover all `\hbox`es). + \_cod + +\_def\.hlink[#1]#2{\_bgroup\_def\#{\_csstring\#}% + \_edef\.type{\.pdfactiontype[#1]}% + \_quitvmode\_pdfstartlink \.linkdimens + attr{\_pdfborder{\.type}}% + user{/Subtype /Link /A \.pdfaction[#1]}\_relax + \_localcolor\.linkcolor{\.type}#2\_pdfendlink\_egroup +} + +\_nspublic \hlink ; + + \_doc + Use `\hlink` as the backing command for OpTeX's \"higher level" linking + commands (`\ilink` and `\ulink`). + + The lower level ones (`\xlink` and its predecessor `\link` actually have + completely different semantics with regards to color, so we keep them as they + are. + \_cod + +\_protected\_def\_ilink[#1]#2{\.hlink[#1]{#2}} +\_protected\_def\_ulink[#1]#2{{\_escapechar=-1 \_ea}\_expanded + {\_noexpand\.hlink[url:\_detokenize{#1}]}{#2}} + +\_public \link \ilink \ulink ; + +%\_protected\_def\_link[#1]#2#3{\_hlink[#1]{#3}} +%\_protected\_def\_xlink#1#2#3#4{\_hlink[#1:#2]{#4}} + + \_doc + Two customizations of `\hlinks` are possible: + \begitems + * Dimensions of rectangular areas created by + `\pdfstartlink`/`\pdfendlink`. This is done using \`\.linkdimens` + (analogous to \OpTeX's `\linkdimens`). Dimensions that are unset are taken + from the respective `\hbox`es. \`\lininglinks` sets the dimensions for + running text~-- it covers all space of a line using `\baselineskip`. + \`\nolininglinks` sets no dimensions, this is useful for buttons, that may + have larger height/depth than a line. + * The color is determined from the type of link (that is, the first action in + <action spec>) by checking `\_<type>linkcolor` (compatible with \OpTeX/). + As a fallback `\_ilinkcolor` is used (set by \OpTeX's `\hyperlinks`) for all links + except for URLs, where `\_elinkcolor` is used instead. If even these + fallback colors are not defined (`\hyperlinks` isn't used), then the most + generic `\_linkcolor` will be taken or no color will be set. + \enditems + \_cod + +\_def\.lininglinks{% + \_def\.linkdimens{height.75\_baselineskip depth.25\_baselineskip}% +} +\_def\.nolininglinks{\_def\.linkdimens{}} +\.lininglinks + +\_nspublic \lininglinks \nolininglinks ; + +\_def\.linkcolor#1{\_trycs{_#1linkcolor}{\_trycs{_ilinkcolor}{\_trycs{_linkcolor}{}}}} + + % \_urllinkcolor = \_elinkcolor with fallbacks +\_def\_urllinkcolor{\.linkcolor{e}} + + \_doc + \secc Open action + The document itself has one action defined in the document catalog. It is + called `/OpenAction`. We allow the user to set it using the familiar + <action spec> syntax with the command \`\openaction``[<action spec>]`. + + Internally we could directly set it by appending to the catalog + using the primitive `\pdfcatalog`, but \LuaTeX/ (pdf\TeX/ really) allows + setting the action with special syntax. This has the benefit that it is not + allowed to set the action more than once. + \_cod + +\_def\.openaction[#1]{\_pdfcatalog{} openaction user{\.pdfaction[#1]}\_relax} +\_nspublic \openaction ; + + \_doc + \label[actions-jump] + \secc Jump actions + + These are the most typical actions. Even \LuaTeX/ itself handles them, + although we don't use the possibility for maintaining generality. There are a + few types of jump actions: + \begitems + * `/GoTo` actions are the classic internal links to named destinations in the + PDF file (created by `\pdfdest` primitive or \OpTeX/'s `\dest`). The + destination names include also the type of internal link (e.g. + `ref:section1`). They are handled by \`\.ilinkaction``[<type>:<name>]`. + * `/URI` actions which are in most cases used as \"goto URL" actions. These + are not that useful directly, because special characters should be handled + before this actions is used (like with `\url`). The low level use is + \`\.urlaction``[url:<url>]`. + * \"Goto remote" actions, which can jump to a destination in another PDF file~-- + either determined by name, or by page number. The external files are + expected to be defined by `\filedef` (but not the embedded variant). The + use is either \`\.extrefaction``[extref:<name>:<named destination>]` for + links to named destination or \`\.extpgrefaction``[extpgref:<name>:<page number>]` + for page destinations. Customization is possible with \`\.extrefextra`, by + default opening in a new windows is requested. + \enditems + \_cod + +\_def\.ilinkaction[#1:#2]{/S /GoTo /D (#1:#2)} + +\_def\.urlaction[#1:#2]{/S /URI /URI (#2)} + +\_def\.extrefaction[#1:#2:#3]{/S /GoToR + /F \.cs{filespec:#2} + /D (#3) + \.extrefextra +} +\_def\.extpgrefaction[#1:#2:#3]{/S /GoToR + /F \.cs{filespec:#2} + /D [\_the\_numexpr#3-1\_relax\_space /Fit] + \.extrefextra +} + +\_def\.extrefextra{/NewWindow true} + + + \_doc + Transition action is not really a jump action in of itself, but is only + useful when chained after jump actions, so we define it here. Transitions + (as page attributes) are handled more thoroughly in section~\ref[transitions]. + + The use would look something like:\nl + \`\.transitionaction`% + `[transition:<animation type>:<duration>:<raw PDF attributes>]`, where all fields + omitted from right take the default values. + \_cod + +\_def\.transitionaction[#1:#2]{/S /Trans \.attrorempty{Trans}{\.maketrans[#2]}} + + \_doc + \secc Named actions + + User can request arbitrary \"named" action with + \`\.namedaction``[named:<name>]`. See user documentation for details. + \_cod + +\_def\.namedaction[#1:#2]{/S /Named /N /#2} + + \_doc + \secc JavaScript actions + + JavaScript actions have two forms, either \`\.jsaction``[js:<name>]` or + `\.jsaction``[js:<script>]`. The first variant uses contents of `\filedef`'d + <name>, the second one uses <script> directly. There is no special catcode + handling. + + \_cod + +\_def\.jsaction[#1:#2]{/S /JavaScript + /JS \_ifcsname _pdfextra_filestream:#2\_endcsname \_lastnamedcs \_else + (#2) + \_fi +} + + \_doc + \sec Page attributes + + PDF represents pages as dictionaries. The dictionaries get generated by \LuaTeX/, + which fills in some attributes {\em attributes} (like `/Content` with + contents of the page and `/Annots` with array of annotations). We can add + more using `\pdfpageattr` primitive token list register. While not that many + are generally useful, there are a few interesting ones. For example + transitions can be set using page attributes, or we might want to set + additional actions (`/AA`) to listen for page events. + + While the so called \"page objects" are in a tree structure (for fast lookup), + only the leaves are real \"pages". PDF allows some attributes to be inherited + from parent page objects, but not all of them and certainly not those we are + interested in. + + The mechanism introduced in this section is optional, because it takes + complete control over `\pdfpageattr`. It gets activated when + \`\initpageattributes` is first used (which happens automatically for some + functionality exposed by this package), but may be activated by the user for + any other purpose. Only attributes listed in \`\pageattributes` are + processed. + + We set the attributes anew for each page, by hooking into \OpTeX/'s + `\_begoutput`. Because `\pdfpageattr` token list doesn't get expanded before + written out to PDF, we expand it using the assignment in `\edef` trick. The + token list gets expanded, but the assignment is not made until it reaches + main processor when the temporary control sequence gets expanded. + \_cod + +% pdfpagattr managament (default for all pages vs current page override) +\_def\.pageattributes{{Trans}{Dur}{Rotate}{AA}} +\_def\.initpageattributes{% + % add hook for setting primitive \pdfpageattr + \_addto\_begoutput{\_edef\.tmp{\_pdfpageattr={\.pdfpageattributes}}\.tmp}% + % no need to do this twice + \_let\.initpageattributes=\_relax +} +\_nspublic \pageattributes \initpageattributes ; + + \_doc + The user interface we want to expose has two parts: + \begitems + * setting the page attribute for just this one page (\`\.pdfcurrentpageattr`), + * setting the default attribute (used when current page value is not set) + (\`\.pdfdefaultpageattr`). + \enditems + + The first one of course brings in the typical \TeX/ problem of knowing the + page where something occurs. As always, the page number contained in + `\gpageno` during processing of said content may of course not actually be + the number of the page where the content ends up! Hence, we need to note the + page number with a delayed write, using `\.setpageof` and later `\.pageof`. + The different settings of page attributes should have distinct names, we use + the \`\.pageattrcount` counter for this. + \_cod + +\_newcount\.pageattrcount +\_def\.pdfcurrentpageattr#1#2{\.initpageattributes + \_incr\.pageattrcount + \.setpageof{pageattr:\_the\.pageattrcount}% + \.sxdef{pdfpgattr:\.pageof{pageattr:\_the\.pageattrcount}:#1}{#2}% +} +\_def\.pdfdefaultpageattr#1#2{\.initpageattributes + \.sxdef{pdfpgattr:#1}{#2}% +} + + \_doc + Finally, the macro \`\pdfpageattributes` takes care of setting generating the + contents of `\pdfpageattr`. For each attribute in `\pageattributes` it first + checks its current page value, only then the default value. If neither is + set, nothing is added. + \_cod + +\_def\.pdfpageattributes{\_ea\.pdfpageattributesA\.pageattributes\.end} +\_def\.pdfpageattributesA#1{\_ifx\.end#1\_else + % use current page override or "default" + % don't emit anything if the value is empty + \.attrorempty{#1}{% + \.trycs{pdfpgattr:\_the\_gpageno:#1}{\.trycs{pdfpgattr:#1}{}}% + }% + \_ea\.pdfpageattributesA\_fi +} + + \_doc + Each attributes then has two switches for the respective default and current + values. For defining a few of them a helper is introduced:\nl\indent + \`\.pdfpageattributesetters`` <attribute> \<default setter> \<current setter> {<value>}`, + \nl + where <attribute> is name of the attribute without the slash + (e.g. `MediaBox`), the two control sequences name the future user setters, + which will take single argument in brackets + (e.g. `\mediabox` and `\thismediabox`) and the <value> can use the + argument. + \_cod + +\_def\.pdfpageattributesetters#1 #2#3#4{% + \.sdef{\_csstring#2}[##1]{\.pdfdefaultpageattr{#1}{#4}}% + \.sdef{\_csstring#3}[##1]{\.pdfcurrentpageattr{#1}{#4}}% + \_nspublic #2 #3 ; +} + + \_doc + Some of the useful attributes are `/Rotate`, which rotates the pages visually + (can be set with \`\defaultpagerotate` and \`\pagerotate`), and the additional + actions (`/AA`, see section~\ref[actions-additional], set using + \`\defaultpageactions` \`\pageactions`). + \_cod + +\.pdfpageattributesetters Rotate \defaultpagerotate \pagerotate {#1} + +\.pdfpageattributesetters AA \defaultpageactions \pageactions {\.pdfaactions{#1}} + + \_doc + \label[transitions] + \secc Transitions, page durations + + There are predefined types of transitions, like `/Wipe`, `/Box`, `/Split`, + etc. Most have other customizible attributes~-- usually directions set in + different ways depending on the animation type at hand, but the most + important attribute is the duration of the animation. Parsing friendly user + notation in the form of + `[<animation type>:<duration>:<other raw attributes>]`, where fields from the + right may be omitted to produce the default value, is handled by + \`\.maketrans`. This macro is also used by transition actions + (see~\ref[actions-jump]). The defaults are simply those defined by PDF + standard (no transition, 1 second duration and the respective default + directions). + \_cod + +\_def\.maketrans[#1]{\.maketransA#1:::\.end} +\_def\.maketransA#1:#2:#3:#4\.end{% + \.emptyor{#1}{<</S /\.nonempty \.attrorempty{D}{#2} #3>>} +} + + \_doc + The attribute setters for transitions (\`\transitions`, \`\transition`) are a + simple wrappers. Similiar is the + setting of page duration in seconds after which PDF viewer automatically + advances to the next page (\`\defaultpageduration`, \`\pageduration`). + \_cod + +\.pdfpageattributesetters Trans \transitions \transition {\.maketrans[#1]} + +\.pdfpageattributesetters Dur \defaultpageduration \pageduration {#1} + + \_doc + \label[nametrees] + \sec Name trees -- attachments and document level JavaScript + + These don't have any last place to be in, so they are documented separately, + here. Attaching files using `/FileAttachment` annotations: + \begitems \style n + * is intended more towards viewers of the document for extra additions and + * doesn't work in the viewers as well as one would like. + \enditems + + That is why instead embed files using normal `\filedef` and then allow them + to be added to the document level `/EmbeddedFiles` entry, which means they + will be shown in the user interface by PDF viewers. `/EmbeddedFiles` is a + document level name tree (contained inside `/Names` entry of `/Catalog`) that + maps names of files to their objects. Although we simplify matters by + constructing more of an array. + + What works very similiarly is document level JavaScript. It is a name tree + within `/JavaScript` field. It maps names of JavaScript actions to their + object numbers. The names aren't very useful, but the actions have their + purpose. They are executed in turn after the document is opened. Hence they + can be used to predefine JavaScript functions in the global context, to be + used later within actions explicitly activated by the user. + + The user level commands are \`\attach``[<name>]` (to attach a previously + `\filedef`'d name with fallback to embedding now if it is a valid path) and + \`\dljavascript``[<name>]{<script>}` (adds action that executes <script> + after document is opened, <name> is more or less meaningless). + + Internally the commands construct lists of what ends up in the resulting name + array, i.e. pairs {\visiblesp `(<name>) <object number> 0 R `}. Intermediate + macros \`\.embeddedfiles` and \`\.dljavascripts` are used for this. + + In the case of file attachments, nothing happens if file is defined and not + found by the fallback. + + \_cod + +% file attachment +\_def\.embeddedfiles{} +\_def\.attach[#1]{\.isfiledefined{\.name}\_iftrue + \.xaddto\.embeddedfiles{(#1) \.cs{filespec:#1} }\_fi +} +\_nspublic \attach ; + +\_def\.dljavascripts{} +\_def\.dljavascript[#1]#2{% + \_immediate\_pdfobj{<< \.jsaction[js:{#2}] >>}% + \.xaddto\.dljavascripts{(#1) \_the\_pdflastobj \_space 0 R }% +} +\_nspublic \dljavascript ; + + \_doc + Renditions (see \ref[mm-renditions]) also need their name tree. This package + mostly doesn't play well with Unicode filenames, that is why they are + forbidden. However, Renditions that are accessed from JavaScript have to be + named/present in a `/Renditions` name tree, with the names encoded in the PDF + encoding (UTF-16BE).\nl + The names and object references are collected in \`\.renditions`. Unicode + encoding is hacked with \`\.pdfstringtounicode`. + \_cod + +\_def\.pdfstringstrip(#1){#1}% +\_def\.pdfstringtounicode#1#2{% + \_ea\_pdfunidef\_ea#1\_ea{\_ea\.pdfstringstrip\_expanded{\.cs{filename:#2}}}% +} +\_def\.renditions{} + + \_doc + Object creation, which is common to all name trees, is handled by\nl\indent + \`\.makenamearray``<name tree name><name tree content>`.\nl + It doesn't do anything + for empty lists, to not bloat PDF files when this mechanism isn't used. + \_cod + +\_def\.makenamearray#1#2{\_ifx#2\_empty\_else + \_immediate\_pdfobj {<< /Names [ #2 ] >>}% + \_pdfnames{/#1 \_the\_pdflastobj \_space 0 R }\_fi +} + + \_doc + The lists themselves can only be written out to the PDF file at the very end + of the run. We use \OpTeX/'s `\_byehook`, which is run in `\_bye`. But `\bye` + itself may be predefined by the user, for example when using some of the + \OpTeX/ tricks. We just hope that the user keeps `\_byehook`. + \_cod + +\_addto\_byehook{% + \.makenamearray{EmbeddedFiles}\.embeddedfiles + \.makenamearray{JavaScript}\.dljavascripts + \.makenamearray{Renditions}\.renditions +} + + \_doc + \sec Viewer preferences + + There are a few customizations of display (and other preferences of PDF + viewers) possible in the document catalog or its subdictionary + `/ViewerPreferences`. Most are not that useful. The interesting + ones are implemented by \`\fullscreen`, \`\showoutlines`, \`\showattached`. + They all set the page mode using \`\.setpagemode`. We don't handle respecting + the last setting (using \`\_byehook`). To prevent invalid PDF files, we set + `\.setpagemode` to `\_relax` after use. + \_cod + +\_def\.setpagemode#1{\_pdfcatalog{/PageMode /#1}\_glet\.setpagemode=\_relax} + +\_def\.fullscreen{\.setpagemode{FullScreen}} +\_def\.showoutlines{\.setpagemode{UseOutlines}} +\_def\.showattached{\.setpagemode{UseAttachments}} + +\_nspublic \fullscreen \showoutlines \showattached ; + + \_doc + Only the setting of duplex / double sided printing and display is in the + nested dictionary. It is handled by \`\duplexdisplay`. The simplistic version + does not handle more attributes in `/ViewerPreferences`. We also set the + meaning to `\_relax` to prevent more (erroneous) uses. + \_cod + +\_def\.duplexdisplay{\_pdfcatalog{% + /PageLayout /TwoPageRight + /ViewerPreferences << + /Duplex /DuplexFlipLongEdge + >>}% + \_glet\.duplexdisplay=\_relax +} + +\_def\duplexdisplay{\.duplexdisplay} + + \_doc + \sec Multimedia + + PDF essentially allows insertion of different types of multimedia: + \begitems + * images, + * audio/video, + * 3D art. + \enditems + + The first is pretty standard and handled normally by the engine (\LuaTeX). + Others are possible, but have to be done manually according to one of the + mechanisms specified by PDF standard: + + \begitems + * Sounds (audio only), + * Movies (video and/or audio), + * Renditions (video and/or audio), + * 3D annotations (3D art), + * Rich Media (video and/or audio, 3D art) + \enditems + + Sadly all these mechanisms are badly flawed, each in different ways. At least + we try to use the one that works in the viewers. + + For audio/video \"Movies" are the simplest mechanism, but they have been + deprecated in PDF 2.0 and no longer work in Acrobat/Foxit (same for + \"Sounds"). + + \"Renditions" are complicated, partly dependant on JavaScript, but at least + supported by Acrobat, Foxit, Evince and Okular. + + \"Rich media" annotations were designed for Flash. This use case is no longer possible + today, but the obscurities remain. They are unnecessiraly complicated, but can + be used without Flash too. Although the result is very plain for audio/video~-- + no controls can be displayed and there are no associated actions. + + \"3D annotations" are reasonably simple, but also flawed. They cannot reuse + embedded file as a source for 3D data. Hence it is better and more consistent + to use Rich Media for 3D annotations. It even has additional benefits, like + the possibility of using multiple initialization scripts. + + In the end, this package exposes two user commands corresponding to two + mechanisms~-- first are Renditions (`\render`) for audio/video that works in most + browsers and Rich Media (`\RM`) mainly for 3D art, but also for audio/video + with limited possibilities. + + Both mechanisms have an annotation at their core. Annotations is essentially + a rectangular area on page. The area corresponds to where the + multimedium will show up. After activating the area somehow (by user click, + or action) the multimedium will start playing. Before annotations the + rectangular area will show something that is called \"normal appearance". + This appearance is of type form XObject. Those are really similiar to pages~-- + they have dimensions, contents made up of PDF graphics operators, \dots, + but they are reusable. Not that useful for annotations where we will need the + form only once, but nice anyways. pdf\TeX/ has primitives for creating them~-- + `\pdfxform` and friends. They essentially do the same code like `\shipout` + does, but instead of page, they make this reusable object. One can then + either use this reusable object in another page/form, but we will indirectly + refer to it for the appearance. + + Important aspect of annotations is that they are really only rectangular + areas on the page, but they are not really part of the page. They sort of sit + on another level and are not influenced by PDF graphic operators which make + the page. In pdf\TeX/ annotations are handled by {\em whatsit} nodes. While + most nodes map to known primitive \TeX/ concepts (like typeset characters, + boxes, rules, etc.) Whatsits are essentially commands for \TeX/ that are + delayed until page is being shipped out (written to PDF file). `\write`, + `\special`, and most pdf\TeX/ commands create whatsits. For annotationos this + is important, because this means that the engine only stores the information + about annotation that we specify, but creates it at due time, when it should + be written to PDF. + + Because whatsits are essentially dimensionless and we want it to be a part of + normal \TeX/ typesetting material we create the annotation (whatsit) in + `\hbox`. This box will be otherwise empty, because the apperance of the + rectangular area is determined by the normal appearance field (`/N` in + `/AP`). We set the dimensions of the box to the dimensions of normal + appearance. Everything will line up nicely, because when processed, the + annotation will take dimensions from the box. + + All of these concepts are implemented in:\nl\indent + \`\.boxedannot``[<type>:<name>]{<appearance>}{<special text>}{<annotation attributes>}`\nl + <type> is used to determine the annotation border (same principle as with + Link annotations, section~\ref[actions-link]), <name> will be used as the + annotation name (`/NM`), <special text> is used for influencing the + `\pdfannot` primitive, and <annotation attributes> will become the body of + the annotation. + + \_cod + +\_def\.boxedannot[#1:#2]#3#4#5{% + \_setbox0=\_hbox{#3}\_setbox2=\_null + \_ht2=\_ht0 \_wd2=\_wd0 \_dp2=\_dp0 + \_preshipout0 \box0 + \_immediate\_pdfxform0 + % box with annotation both stretching to dimensions of appearance + \_hbox{\.setpageof{#1:#2}% + \_pdfannot #4 {#5 + /AP <</N \_the\_pdflastxform \_space 0 R>> + \_pdfborder{#1} + /NM (#2) + /Contents (#1 '#2') + }% + \_copy2 + }% +} + + \_doc + There is another weird thing common to both multimedia mechanisms~-- + the redefinition of `\.name`. It is initially + set by `\.secondoptdef` to <name>, but may be redefined by user supplied + `name` key-value parameter. This should be used when there are multiple uses + of the same content. Otherwise samely named annotations would be + indistinguishable both for PDF viewer and our handling of actions (which + would all refer only to the first instance). + + To somewhat overcome this, trying to use the same <name> (within the same + type of annotaiton) will use dummy name from \`\.unnamedannotcount` (for + uniqueness). This means that <name> will always refer to the first instance. + \`\.redefinename` handles this. + \_cod + +\_newcount\.unnamedannotcount +\_def\.redefinename#1{% + \.isdefined{#1:\.name}\_iftrue + \_incr\.unnamedannotcount + \_edef\.name{\_the\.unnamedannotcount}% + \_else + \_edef\.name{\_kv{name}}% + \_fi +} + + \_doc + \label[mm-renditions] + \secc Renditions (audio/video) + + There are three main types of PDF objects involved in the Renditions + (\"Multimedia") mechanism: + + \begitems + * Screen annotations define the area for playing multimedia. + * Rendition objects define the multimedia to play. + * Rendition actions associate Rendition objects with Screen annotations. + \enditems + + You can theoretically arbitrarily mix and match rendition objects and screen + annotations by invoking different actions. In practice Evince and Okular do + really simplistic parsing and don't fully support the actions fully. But by + keeping it simple it is possible to make it work almost the same in all + viewers that support renditions. + + Different sources of audio/video should be possible. In fact all three file + specifications (embedded files, files specified by URL/path) could work. + Again in practice embedded file is the safest bet, that works in all viewers + that support renditions. + + The user facing command is:\nl\indent + \`\render``[<name>][<optional key-value paramers>]{<horizontal material>}`\nl\noindent + <name> is the friendly name set using `\filedef` or file path if <name> isn't + `\filedef`d and is to be embedded. The key-value parameters in brackets + can be entirely omitted. They can influence the playback (except for `controls` + most are not well supported). Default values are taken from + `\.renderdefaults`. + + `\render` doesn't do anything (except print warning) if file <name> isn't + defined and <name> isn't a path to file that can be embedded. + + The first PDF object it defines is Rendition, which specifies information + about the multimedium (name, file specification, MIME type and options from + key-value parameters). Some of the fields are in `/BE` (\"best effort") + dictionaries. This is due to the very general design of Renditions, which + theoretically allows the PDF viewer to choose from multiple Renditions if + they know they can't support some of the requested features. But that is not + much useful in practice, so we just don't complicate it. + + Next defined object is Screen annotation, which complicates thing by + requiring (`/P`) reference to the page where the annotation is (handled by + `\setpageof` and `\pageof` pair). Important field is `/A` which specifies + actions that shall be executed when the screen area is clicked. We let the + user change the action, but the sensible default of starting to play the + multimedium is used (and this is the only thing that works in some viewers + anyways). Additional actions `/AA` may be used to react to events like mouse + over or page open/close~-- the most probable use case is autoplay on page + open, for which shortcut of \`\renditionautoplay` is defined. + + The code is slightly complicated by the fact, that actions need to reference + the Rendition and Screen objects. In the case of the action contained in + Screen annotation this essentialy involves a self reference. Hence it is + needed to first reserve an object number and later use it for the annotation. + Because the object numbers may also be needed by actions defined later, we need + to save them to `\_pdfextra_rendition:<name>` and `\_pdfextra_screen:<name>` + respectively, but also define aliases with empty names, so users can easily + reference the latest rendition. + + \_cod + +\.secondoptdef\.render#1{\.isfiledefined{\.name}\_iftrue\_bgroup + \_ea\_readkv\_ea{\_ea\.renderdefaults\_ea,\_the\_opt}% + \.colortorgbdef\.bgcolor{\_kv{background}}% + % rendition object ("media specifaction") + \.pdfstringtounicode\.uiname\.name + \_pdfobj {<</Type /Rendition + /S /MR + /N \.uiname + /C <<%/Type /MediaClip + /S /MCD % subtype MediaClipData + /D \.cs{filespec:\.name} + /CT (\.cs{filemime:\.name}) + /P << /TF (TEMPALWAYS) >> % allow creating temporary files + >> + /P <<%/Type /MediaPlayParams + /BE << /C \_kv{controls} /V \_kv{volume} /RC \_kv{repeat} >> + >> + /SP <<%/Type /MediaScreenParams + /BE << /O \_kv{opacity} /B [\.bgcolor] >> + >> + >>}\_pdfrefobj\_pdflastobj + \.xaddto\.renditions{\.uiname \_the\_pdflastobj \_space 0 R }% + \.redefinename{rendition}% + \.sxdef{rendition:\.name}{\_the\_pdflastobj}% + % screen annotation ("screen space allocation") + \_pdfannot reserveobjnum% "self" reference will be needed inside screen annot. + \.sxdef{screen:\.name}{\_the\_pdflastannot}% + % aliases to latest rendition/screen with empty name + \_global\.slet{rendition:}{rendition:\.name}% + \_global\.slet{screen:}{screen:\.name}% + \_edef\.action{\_kv{action}}\_edef\.aactions{\_kv{aactions}}% + \.boxedannot[rendition:\.name]{#1}{useobjnum\_the\_pdflastannot}{% + /Subtype /Screen + % reference to page of the rendition (\setpageof done by \.boxedannot) + % the spaces are weird, but \pdfpageref eats them + /P \_pdfpageref\.pageof{rendition:\.name} \_space 0 R + /A \_ea\.pdfaction\_ea[\.action] + /AA \_ea\.pdfaactions\_ea{\.aactions} + }% + \_egroup\_fi +} +\_nspublic \render ; + + \_doc + Here are the defaults used for `\render`~-- \`\.renderdefaults`. Users can + redefine them all together or override as needed with key-value parameters. + The defaults correspond to values specified by PDF standard. Other values may + not be respected by all viewers. + \_cod + +\_def\.renderdefaults{% + name=\.name, + controls=false, + volume=100, + repeat=1, + opacity=1.0, + background=1 1 1, + action=rendition::play, + aactions={}, +} + + \_doc + Most probable use of additional actions is to start auto-start playing of the + multimedium. For this purpose \`\renditionautoplay` is defined as a shorthand + for action to play the lastly defined rendition on page visible event. + \_cod + +\_def\.renditionautoplay{{PV}{rendition::play}} +\_nspublic \renditionautoplay ; + + \_doc + \seccc Rendition actions + + Rendition actions unfortunately use cryptic symbolic numbers (`0`, `1`, `2` + and `3`) for actions that could be called `play`, `stop`, `pause` and + `resume` respectively. Except for these predefined actions (that use `/OP`) + running of JavaScript is possible using `/JS (<script>)` with potential + fallback to `/OP`. This is dangerous teritory, because support of the right + API in the viewer is very low. Although it is possible to define such action + type by: + + \begtt +\.sdef{renditionaction:myaction}{/JS (app.alert("something useful");) /OP 0} + \endtt + + The use of rendition action is: + \`\.renditionaction``[rendition:<name>:<action type>]`. Empty name refers to + last rendition, so e.g.`\.renditionaction``[rendition::pause]` is possible. + \_cod + +\.sdef{renditionaction:play}{/OP 0} +\.sdef{renditionaction:stop}{/OP 1} +\.sdef{renditionaction:pause}{/OP 2} +\.sdef{renditionaction:resume}{/OP 3} +\_def\.renditionaction[#1:#2:#3]{/S /Rendition + \.cs{renditionaction:#3} + /R \.cs{rendition:#2} 0 R + /AN \.cs{screen:#2} 0 R% +} + + \_doc + \secc Rich Media (3D/audio/video) + + Some principles seen with Renditions (section~\ref[mm-renditions]) apply here + too. But additionally we deal with 3D specifics and unfortunate Flash + leftovers. + + Unlike Renditions both page area and multimedium specifaction are handled in + a single annotation~-- the Rich Media annotation. The code is unfortunately + obscured due to the weird requirements, but this is essentially what we are + trying to create with \`\RM`: +\begtt +/Type /Annot +/Subtype /RichMedia +/RichMediaSettings << + /Activation << + /Condition /PV + /Scripts [ 14 0 R ] + >> + /Deactivation << /Condition /XD >> +>> +/RichMediaContent << + /Assets << /Names [ (kladka.prc) 2 0 R (wireframe.js) 14 0 R ] >> + /Configurations [ << + /Type /RichMediaConfiguration + /Subtype /3D + /Instances [ << + /Type /RichMediaInstance + /Subtype /3D + /Asset 2 0 R + >> ] + >> ] +>> +\endtt + + The activation/deactivation will be dealt with later. But we see that to + insert a simple 3D file, we have to pack it inside a file specification + (indirect reference to object `2 0 R`), then in \"instance", inside a + \"configuration" inside \"content". As if it wasn't enough the names + (normally contained in the file specification) have to be specified again in + `Assets` name tree that uselessly maps names to file specifications. Because + this is a 3D Rich Media annotation there are other files at play~-- + initialization scripts. These are specified in `/Scripts` and are executed in + turn when the annotation is activated. Not shown is, that some + \"configurations" and \"instances" actually have to be specified indirectly. + + If it wasn't for Flash we could do with something like: + +\begtt +/Type /Annot +/Subtype /RichMedia +/Activation /PV +/Scripts [ 14 0 R ] +/Deactivation /XD +/Content 2 0 R +\endtt + + Which contains equivalent information. But unfortunately here we are\dots + \_cod + +\.secondoptdef\.RM#1{\.isfiledefined{\.name}\_iftrue + \_edef\.tmp{\.cs{filemime:\.name}}% + \_edef\.subtype{\_ea\.mimetormsubtype\_ea[\.tmp]\_space}% + \_ifx\.subtype\_space + \_opwarning{unknown rich media type for '\.name', ignored}\_else + \_bgroup + \_ea\_readkv\_ea{\_ea\.RMdefaults\_ea,\_the\_opt}% + % Instance that has the media file as an asset + \_pdfobj {<</Type /RichMediaInstance + /Subtype /\.subtype + /Asset \.cs{filespec:\.name} + >>}\_pdfrefobj\_pdflastobj + % Configuration with one single instance (the above) + \_pdfobj {<</Type /RichMediaConfiguration + /Subtype /\.subtype + /Instances [ \_the\_pdflastobj \_space 0 R ] + >>}\_pdfrefobj\_pdflastobj \_edef\.configuration{\_the\_pdflastobj}% + \_edef\.names{\.cs{filename:\.name} \.cs{filespec:\.name} }% initial asset + \.redefinename{rm}% + \_def\.scriptfilespecs{}% + \_edef\.views{\_kv{views}}\_edef\.scripts{\_kv{scripts}}% + \_ifx\.views\_empty \_edef\.views{\.name}\_fi + \_ea\.DDDscripts\_ea{\.scripts}% + % annotation in hbox + \.boxedannot[rm:\.name]{#1}{}{% + /Subtype /RichMedia + /RichMediaSettings << + /Activation << + /Condition \.cs{activation:\_kv{activation}} + \.emptyor{\.scriptfilespecs}{/Scripts [ \.nonempty ]} + /Presentation << /Toolbar \_kv{toolbar} \.RMpresentationextra >> + >> + /Deactivation << /Condition \.cs{deactivation:\_kv{deactivation}} >> + >> + /RichMediaContent << + /Assets << /Names [ \.names ] >> + /Configurations [ \.configuration \_space 0 R ] + \.emptyor{\_ea\.DDDviews\_ea{\.views}}{/Views [ \.nonempty ]} + >> + }% + \.sxdef{rm:\.name}{\_the\_pdflastannot}% + \_global\.slet{rm:}{rm:\.name}% + \_egroup\_fi\_fi +} +\_nspublic \RM ; + + \_doc + The code is similiar to `\render`, but we also ignore everything if we don't + recognize the type of media (`Video`, `Sound` or `3D`). For that we use a + simple mapping from MIME types with \`\.mimetormsubtype`. This means that + although we aim Rich Media mostly for 3D art it may also be used for Video + and Sound. + \_cod + +\_def\.mimetormsubtype[#1/#2]{\.cs{rmtype:#1}} + +\.sdef{rmtype:model}{3D} +\.sdef{rmtype:video}{Video} +\.sdef{rmtype:audio}{Sound} + + \_doc + Then we also need to construct the weird name \"tree" (essentialy an array in + our case) and script array. \`\.DDDscripts` and \`\.DDDviews` do this. + Name tree is accumulated in `\.names`, and starts with the media file. After + that each script is added to \`\.names` and \`\.scriptfilespecs`. The scripts + are passed as a comma separated array. Ignoring initial spaces is done using + undelimited-delimited argument pair trick. + \_cod + +\_def\.DDDscripts#1{\.DDDscriptsA#1,,,\.end} +\_def\.DDDscriptsA#1#2,{\_ifx,#1\_ea\.untilend\_else + \.isfiledefined{#1#2}\_iftrue% + \_addto\.scriptfilespecs{\.cs{filespec:#1#2} }% + \_addto\.names{\.cs{filename:#1#2} \.cs{filespec:#1#2} }% + \_fi + \_ea\.DDDscriptsA\_fi +} + + \_doc + For 3D views we need to process yet another comma separated list, this time + with \`\.DDDviews`. The result has to be separated by spaces and we also + don't want to emit something if the specified view was invalid. Unfortunately + this is expansion only context, so we can't issue a warning. + + As a user convenience, before `\.DDDview` is executed, view with the name of + `\.name` is tried instead of empty view array. This means that for simple 3D + art with one view, one can create view with the same name as the 3D object + and not have to specify anything. We also take the name only after it is + redefined from optional key-value parameters~-- this is so we can support + even the case of e.g. `screw` 3D model used twice, once with `name=screw1`, + another time with `name=screw2` (with the corresponding `screw1` and `screw2` + views). This is probably less useful, but\dots + \_cod + +\_def\.DDDviews#1{\.DDDviewsA#1,,,\.end} +\_def\.DDDviewsA#1#2,{\_ifx,#1\_ea\.untilend\_else + \.isdefined{3dview:#1#2}\_iftrue + \_lastnamedcs\_space \_fi + \_ea\.DDDviewsA\_fi +} + + \_doc + The activation/deactivation names are kind of cryptic, so we give them + descriptive names. Default is explicit (de)activation. Instead of `/PV` (page + visible) and `/PI` (page invisible) it would be possible to use \"page open" + and \"page close". These are slightly different in cases when more pages are + shown on screen at once, because only one page is \"open", while multiple are + \"visible". + \_cod + +\.sdef{activation:explicit}{/XA} +\.sdef{activation:auto}{/PV} +\.sdef{deactivation:explicit}{/XD} +\.sdef{deactivation:auto}{/PI} + + \_doc + Additional means of customization are here. \`\.RMdefaults` contains the + default key-value parameters. \`\.RMpresentationextra` can be used to set + more attributes in `/RichMediaPresentation` dictionary (although those are + more specific and not generally useful). + \_cod + +\_def\.RMdefaults{% + name=\.name, + activation=explicit, + deactivation=explicit, + toolbar=true, + views=, + scripts=, +} +\_def\.RMpresentationextra{} + + \_doc + For scripting using JavaScript actions one needs to access the 3D context of + the 3D / Rich Media annotation. This requires the page number. We can't use + `this.pageNum`, because the script strictly doesn't have to be on + the same page. We use `\.pageof` (`\.setpageof` was done in `\.boxedannot`) + to retrieve the page number in next run. Also PDF indexes page numbers from + 0. \`\DDDannot``{<name>}`. and \`\DDDcontext``{<name>}` allow this. + \_cod + +\_def\.DDDannot#1{% + this.getAnnotRichMedia(\_the\_numexpr\.pageof{rm:#1}-1\_relax, '#1')% +} +\_def\.DDDcontext#1{\.DDDannot{#1}.context3D} + +\_nspublic \DDDannot \DDDcontext ; + + \_doc + \label[mm-3dviews] + \secc 3D views + + This is the interesting part about 3D art. They can have a set of predefined + views~-- although a user may start from one, they can interactively change + all the aspects by dragging with mouse or messing with the settings shown by + right click menu. + + There are several transformations that have to be done before it is possible + to display 3D scene on a computer screen: + + \begitems \style n + * 3D transformation from the coordinate system of 3D artwork (\"model") to the \"world + coordinate system". + * 3D transformation from the world coordinate system to camera coordinate + system. + * projection to 2D (3D to 2D transformation). + \enditems + + When talking about PDF, positive $x$ goes to the right, positive $y$ up, and + positive $z$ \"away" from us (\"into the page"). This means we are working + with a left handed coordinate system. In camera space, the camera sits at + $(0, 0, 0)$ facing towards positive $z$ with positive $x$ and $y$ going right + and up respectively. Projection (one way or another) discards the $z$ + coordinate. + + Although the transformations are not strictly linear, they are essentially + done using multiplication by {\em transformation matrices}. The matrix for + \"model to world" (or \"model") transformation is part of the 3D art file and can't + be changed. However, we can make it up, because we can fully control the + second transformation (\"world to camera" or \"view" transformation)~-- + although we don't specify the \"world to camera" matrix but rather its + inverse, the \"camera to world" matrix (`/C2W`). This matrix has the $4 \times 4$ + form, which also allows {\em linear transformation} and {\em translation}: + + $$ + M_{c2w} = \pmatrix{a & d & g & t_x \cr b & e & h & t_y \cr c & f & i & t_z \cr 0 & 0 & 0 & 1} + $$ + + Here we use the column major convention, which is also the order how we would + write the matrix to PDF file, where it is an array of 12 elements: + + \begtt \catcode`\$=3 + /C2W [$a$ $b$ $c$ $d$ $e$ $f$ $g$ $h$ $i$ $t_x$ $t_y$ $t_z$] + \endtt + + In the rendering pipeline everything is transformed from world coordinates to + camera space coordinates. We can think about the process also in the other + way. Using $M_{c2w}$ we specify camera's position and orientation in the + world coordinate system. Due to how transformation using matrix + multiplication works, the first column in the $M_{c2w}$ matrix (vector $(a, + b, c)^T$) specifies how \"positive $x$ direcetion" (\"right") from camera + space ends up in world coordinate system. Similiarly for $(d, e, f)^T$ being + the image of positive $y$ (\"up") and $(g, h, i)^T$ being the image of + positive $z$ (\"forward"). The last column, $(t_x, t_y, t_z)^T$ represents + translation from camera space to world. Translation of origin (camera + position) will leave it in the point with coordinates $(t_x, t_y, t_z)$. + Because of these associations with the intuitive meanings of $x$, + $y$, $z$ in camera space we also sometimes call the vectors in the first + three columns of $M_{c2w}$ \"right", \"up" and \"forward" and the last one + \"eye": + + $$ + \vec{R} = \pmatrix{a \cr b \cr c}\!, \quad + \vec{U} = \pmatrix{d \cr e \cr f}\!, \quad + \vec{F} = \pmatrix{g \cr h \cr i}\!, \quad + \vec{E} = \pmatrix{t_x \cr t_y \cr t_z}\!. + $$ + + We usually want $\vec{R}$, $\vec{U}$ and $\vec{F}$ to form an orthonormal set + of vectors, i.e. all of unit length and each pair is orthogonal. The + orthoganility will come from the way we calculate them, but the normality has + to be ensured by normalizing the vectors after computing them, which will not + be explicitly written out in the following text. $\vec{E}$ is a positional, + not directional, vector and it's length will be preserved. + + Now we only need a convenient way to calculate all four vectors. A wide spread + method is sometimes called \"look at". It essentially involves having two + points: \"eye" ($E$, position of the camera) and \"target" ($T$, the point + where the camera is pointing at). The camera position is already provided: + $$ + \vec{E} = E + $$ + From these two points alone we can easily calculate the forward vector, which + corresponds to the direction of the camera: + $$ + \vec{F} = \overrightarrow{ET} = T - E + $$ + When we now imagine the point $E$ and vector $\vec{F}$ looking towards $T$ we + can see that there is a degree of freedom~-- the camera can rotate about the + forward vector. There is no other way than to arbitrarily choose + either up or right vector. Usually we choose an arbitrary \"global up" vector + $U_G$, which will influence the general direction of the final up vector. + This is because we use it to calculate the right vector: + $$ + \vec{R} = \vec{U}_G \times \vec{F} + $$ + The cross product makes it so that: + \begitems \style n + * $\vec{R}$ is perpendicular to $\vec{F}$ + * it is also perpendicular to global up vector ($\vec{U}_G$) which we used to + get rid of remaining degree of freedom. + \enditems + Now that we have two orthonormal vectors (with the normalization not being + explicit) we can calculate the remaining up vector: + $$ + \vec{U} = \vec{F} \times \vec{R} + $$ + + The last mysterious part about the calculation are the cross products. They + are of course not commutitative, so why e.g. $\vec{U}_G \times \vec{F}$ and + not the other way around? This is because we have to preserve the relations + these vectors had as directions of positive axes of the original camera + space. There we had positive $x$ going right, positive $y$ up and positive + $z$ forward in a left handed coordinate system. This means that following + holds (according the left hand rule): + + $$\eqalign{ + \vec{R} &= \vec{U} \times \vec{F} \cr % j * k = i + \vec{U} &= \vec{F} \times \vec{R} \cr % k * i = j + \vec{F} &= \vec{R} \times \vec{U} \cr % i * j = k + }$$ + + The scheme has one flaw though. When the directions of global up vector + $\vec{U}_G$ and forward vector $\vec{F}$ are linearly dependent the computed + right vector will be $(0, 0, 0)$. Hence some handling of this special case is + needed. + + The \"look at" method is essentially what is used in Alexander Grahn's + package movie15\fnote{\url{https://www.ctan.org/pkg/movie15}}. Although the + input aren't two points, but rather a \"center of orbit" point ($COO$, our + \"target"), \"center of orbit to camera vector" ($\overrightarrow{C2C}$, + default is $(0, -1, 0)$) and distance of camera from the center of orbit + ($ROO$). The value used for the arbitrary \"global up" vector is $(0, 0, 1)$. + When forward vector is $(0, 0, z)$, then global up is chosen to be $(0, -1, + 0)$ or $(0, 1, 0)$ to handle the \"0 right vector" issue. + + Because the movie15 method of providing the parameters is used in essentially + all packages that handle PDF 3D art (movie15, media9, rmannot, Con\TeX/t) we + also follow the suite. + + \`\DDDview`[<view name>][<key-value parameters>] is the command for defining + 3D views. These have to be saved into separate PDF objects anyways, using + this interface we allow their reuse. If <view name> is same as <name> of + `\RM` argument and no other views are specified <view name> view is + automatically used (see `\RM` for details). + + Key-value parameters are not optional this time, because rarely one suffices + with default values~-- different 3D views are about customization. Handling + of them is not as straightforward as before. We initially read the key-value + parameters only to determine the `method` used for calculating the `/C2W` + matrix. Then we reread key-value parameters again, this time with also with + the default values for this particular method. Not that the general 3d views + details are changed, but the methods themselves have key-value parameters of + their own, and we support specifying them in this \"flat" way. + + Additionally we allow the different methods used to compute `/C2W` to not be + expandable. Hence they are executed outside of expansion only context and are + fully processed~-- the text they add to 3D view PDF object is temporarily + stored in \`\.viewparams`. + + The rest is simply setting sensible defaults (or user overrides) for + internal/external name of the view (`/IN` and `/XN`, one is used for + scripting, one is shown by the PDF viewer), background color, rendering mode, + and lighting. Cross sections and nodes are currently not supported, although + users can hook in their own code using `\.DDDviewextra`, + `\.DDDrendermodeextra` or `\.DDDprojectionextra`. + + We have to be careful about setting rendering mode and lighting scheme, + because they normally fall back to the values specified in 3D art file, which + we can't access, so better not set them to anything if they are empty. + \_cod + +\_def\.DDDview[#1][#2]{\_bgroup + \_readkv{\.DDDviewdefaults,#2}% + \_edef\.tmp{\.DDDviewdefaults,\.cs{3dview:\_kv{method}:defaults}}% + \_readkv{\.tmp,#2}% + \.colortorgbdef\.bgcolor{\_kv{background}}% + \.cs{3dview:\_kv{method}}% sets \.viewparams (/MS, /C2W, /CO) + \_pdfobj {<</Type /3DView + /XN (#1) + /IN (#1) + \.viewparams % /MS, /C2W, /CO + /P << + \.cs{3dprojection:\_kv{projection}} + \.DDDprojectionextra + >> + /BG <<%/Type /3DBG + /Subtype /SC + /C [\.bgcolor] + >> + \.emptyor{\_kv{rendermode}}{% + /RM <<%/Type /3DRenderMode + /Subtype /\.nonempty + \.DDDrendermodeextra >> }% + \.emptyor{\_kv{lighting}}{% + /LS <<%/Type /3DLightingScheme + /Subtype /\.nonempty >> }% + >>}% + \_pdfrefobj\_pdflastobj + \.sxdef{3dview:#1}{\_the\_pdflastobj \_space 0 R}% + \_egroup +} + +\_nspublic \DDDview ; + + + \_doc + \`\.DDDviewdefaults` stores default key-value parameters for 3D views. They + are mostly the PDF standard defaults or what movie15/media9 uses (for + compatibility).\`\.DDDviewextra`, \`\.DDDrendermodeextra` or + \`\.DDDprojectionextra` can be used by the users to hook themself into 3D + view object creation. + \_cod + +\_def\.DDDviewdefaults{ + projection=perspective, + scale=1, + ps=Min, + FOV=30, + background=1 1 1, + rendermode=, + lighting=, + method=media9, +} +\_def\.DDDprojectionextra{} +\_def\.DDDrendermodeextra{} +\_def\.DDDviewextra{} + + \_doc + There are two different projection methods: + \begitems + * Orthographic: $z$ coordinate is simply thrown away, `scale` is used for scaling + the result. For technical parts where we want lines that are parallel stay + parallel in the view. + * Perspective: is the way human eye sees. `FOV` can be used to set field of + view. (`ps` parameter for additonal scaling to fit width/height is also + available, but the default is fine for casual users). + \enditems + \_cod + +\.sdef{3dprojection:ortho}{/Subtype /O /OS \_kv{scale}} +\.sdef{3dprojection:perspective}{/Subtype /P /FOV \_kv{FOV} /PS /\_kv{ps}} + + + \_doc + We offer the possibility of setting`/C2W` matrix and `/CO` (distance from + camera to center of orbit) directly using `method=manual`. + \_cod + +\.sdef{3dview:manual:defaults}{ + matrix=1 0 0 0 1 0 0 0 1 0 0 0 , + centeroforbit=0, +} +\.sdef{3dview:manual}{\_edef\.viewparams{ + /MS /M + /C2W [\_kv{matrix}] + /CO \_kv{centeroforbit} +}} + + \_doc + Another simple way of specifying camera position/orientation is to use a + named setting of U3D file using a U3D path with `method=u3d`. + \_cod + +\.sdef{3dview:u3d:defaults}{ + u3dpath=, +} +\.sdef{3dview:u3d}{% + \_pdfunidef\.tmp{\_kv{u3dpath}}% + \_edef\.viewparams{ + /MS /U3D + /U3DPath \.tmp \_space +}} + + \_doc + The most advanced method of setting `/C2W` matrix and `/CO` is + `method=media9`. It is thoroughly explained above, the few differences are + because the input values are not two points. Also for conciseness \"x", \"y" + and \"z" are used instead of right, up and forward. The calculations are + done in Lua, for simplicity. + + We expect the user to supply the numbers in the form \"`1 2 3`", but in Lua we + need them comma separated (\"1, 2, 3"). \`\.luatriplet` does this. Just in + case the code is somehow adapted without ensuring that $x$ and $z$ are + orthonormal, we normalize also $y$ after the second cross product. + \_cod + +\_def\.luatriplet#1 #2 #3 {#1, #2, #3} + +\.sdef{3dview:media9:defaults}{% + roo=0, + coo=0 0 0, + c2c=0 -1 0, +} +\.sdef{3dview:media9}{\_edef\.viewparams{ + /MS /M + /C2W [\_directlua{ + local function normalize(x, y, z) + local len = math.sqrt(x*x + y*y + z*z) + if len \csstring\~= 0 then return x/len, y/len, z/len else return 0, 0, 0 end + end + local function cross(ux, uy, uz, vx, vy, vz) + return uy*vz - uz*vy, uz*vx - ux*vz, ux*vy - uy*vx + end + local function printmat(...) + local arr = table.pack(...) + for k, v in ipairs(arr) do + arr[k] = string.format("\_pcent.6f", v) + end + tex.print(table.concat(arr, " ")) + end + + local roo = \_kv{roo} + local coo_x, coo_y, coo_z = \_ea\.luatriplet\_expanded{\_kv{coo}} + local c2c_x, c2c_y, c2c_z = normalize(\_ea\.luatriplet\_expanded{\_kv{c2c}}) + + local eye_x, eye_y, eye_z = coo_x + c2c_x*roo, coo_y + c2c_y*roo, coo_z + c2c_z*roo + + local z_x, z_y, z_z = -c2c_x, -c2c_y, -c2c_z + + local up_x, up_y, up_z = 0, 0, 1 + if math.abs(z_x) + math.abs(z_y) < 0.0000001 then % z_x == 0 and z_y == 0 + if z_z < 0.0000001 then % z_z <= 0 + up_x, up_y, up_z = 0, 1, 0 + else + up_x, up_y, up_z = 0, -1, 0 + end + end + + local x_x, x_y, x_z = normalize(cross(up_x, up_y, up_z, z_x, z_y, z_z)) + local y_x, y_y, y_z = normalize(cross(z_x, z_y, z_z, x_x, x_y, x_z)) + + local eye_x, eye_y, eye_z = coo_x - z_x*roo, coo_y - z_y*roo, coo_z - z_z*roo + + printmat(x_x, x_y, x_z, y_x, y_y, y_z, z_x, z_y, z_z, eye_x, eye_y, eye_z) + }] + /CO \_kv{roo} +}} + + \_doc + Last, but not least, is an action for setting the 3D view of a 3D/RM annotation + using an action. \`\.goto3dviewaction``[goto3dview:<name>:<view>]`. <name> is + name of the annotation which will be influenced. <view> is passed directly to + PDF. Therefore it can be either an index to the view array (starting at 0) or name + of view in parentheses~-- \"`(<view name>)`". + \_cod + +\.sdef{goto3dviewaction}[#1:#2:#3]{/S /GoTo3DView + /TA \.cs{rm:#2} 0 R + /V #3 +} + + \_doc + \label[mime] + \sec MIME type database + + This is the uninteresting MIME type database teased in section~\ref[files]. + Ideally this would only be a subset of what IANA defines at + \url{https://www.iana.org/assignments/media-types/media-types.xhtml}. + But there are additions like `model/u3d` and `model/prc`, which don't + seem to be official, yet. Other \"unofficial" MIME types are taken from + Mozilla's \"common" lists: + \begitems + * \url{https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types}. + * \url{https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types} + \enditems + + + \`\.mimetype``{<extension>}{<MIME type>}` is a shortcut of mapping + <extension> to <MIME type>. + \_cod + +\_def\.mimetype#1#2{\_sdef{_pdfextra_mimetype:#1}{#2}} + +\.mimetype{js}{application/javascript} +\.mimetype{pdf}{application/pdf} + +\.mimetype{prc}{model/prc} +\.mimetype{u3d}{model/u3d} + +\.mimetype{wav}{audio/x-wav} +\.mimetype{mp3}{audio/mpeg} +\.mimetype{opus}{audio/opus} + +\.mimetype{avi}{video/x-msvideo} +\.mimetype{mp4}{video/mp4} +\.mimetype{webm}{video/webm} + +\_endnamespace +\_endcode diff --git a/macros/luatex/generic/pdfextra/pdfextra.sty b/macros/luatex/generic/pdfextra/pdfextra.sty new file mode 100644 index 0000000000..2ebe8354c5 --- /dev/null +++ b/macros/luatex/generic/pdfextra/pdfextra.sty @@ -0,0 +1,4 @@ +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{pdfextra} + +\input pdfextra diff --git a/macros/luatex/generic/pdfextra/pdfextra.tex b/macros/luatex/generic/pdfextra/pdfextra.tex new file mode 100644 index 0000000000..cbbeca0c17 --- /dev/null +++ b/macros/luatex/generic/pdfextra/pdfextra.tex @@ -0,0 +1,364 @@ +\ifdefined \pdfextraloaded \expandafter \endinput \fi +\chardef\pdfextraloaded=\catcode`\_ +\catcode`\_=11 + +\long\def \_xargs #1#2{\ifx #2;\else + \expandafter#1\expandafter#2\expandafter\_xargs \expandafter #1\fi} + +\def \_private {\_xargs \_privateA} +\def \_privateA #1{\expandafter\let\csname _\csstring #1\endcsname#1} + +% primitives +\_private \detokenize \expanded \hbox \ht \wd \dp \copy \immediateassignment ; +\_private \escapechar \baselineskip \quitvmode \lastnamedcs ; + +% +% prefixed.opm +% + +\_private \expandafter \def \let \ifx \else \fi \undefined \wlog \endinput ; + +\_let\_ea=\_expandafter + +\_def \_codedecl #1#2{% + \_ifx #1\_undefined \_wlog{#2}% + \_else \_ea \_endinput \_fi +} + +\_let \_endcode = \_endinput + +\_def \_wterm {\_immediate \_write16 } + +\_private \ifcsname \csname \endcsname \errmessage \gdef \csstring \directlua ; +\_let \_ea = \_expandafter + +% modified +\_def \_pkglabel{} +\_def\_namespace #1{% + \_ifcsname namesp:#1\_endcsname \_errmessage + {The name space "#1" is used already, it cannot be used twice}% + \_endinput + \_else \_resetnamespace{#1}\_fi +} +\_def\_resetnamespace #1{% + \_ea \_gdef \_csname namesp:#1\_endcsname {}% + \_gdef \_pkglabel{_#1}% + \_directlua{ + luatexbase.add_to_callback("process_input_buffer", + function (str) + return string.gsub(str, "\_nbb[.]([a-zA-Z])", "\_nbb _#1_\_pcent 1") + end, "_namespace") + }% +} +% modified +\_def\_endnamespace {% + \_directlua{ luatexbase.remove_from_callback("process_input_buffer", "_namespace") }% + \_gdef \_pkglabel{}% +} + +\_def \_nspublic {\_xargs \_nspublicA} +\_def \_nspublicA #1{% + \_unless\_ifx #1\_undefined + \_opwarning{\_ea\_ignoreit\_pkglabel\_space redefines the meaning of \_string#1}\_fi + \_ea\_let \_ea#1\_csname \_pkglabel _\_csstring #1\_endcsname +} + +% modified +\_def \_public {\_xargs \_publicA} +\_def \_publicA #1{\_ea\_let \_ea#1\_csname _\_csstring #1\_endcsname} + +% +% basic-macros.opm +% + +\_private \bgroup \egroup \empty \space \null \long ; + +\_long\_def \_ignoreit #1{} +\_long\_def \_useit #1{#1} +\_long\_def \_ignoresecond #1#2{#1} +\_long\_def \_usesecond #1#2{#2} + +\_private \the \inputlineno \edef ; + +\_def \_opwarning #1{\_wterm{WARNING l.\_the\_inputlineno: pdfextra: #1.}} + +\_edef \_bslash {\_csstring\\} +\_edef \_pcent {\_csstring\%} +\_edef \_nbb {\_bslash\_bslash} + +\_private \xdef \begincsname ; + +\_def \_sdef #1{\_ea\_def \_csname#1\_endcsname} +\_def \_sxdef #1{\_ea\_xdef \_csname#1\_endcsname} +\_def \_slet #1#2{\_ea\_let \_csname#1\_ea\_endcsname + \_ifcsname#2\_ea\_endcsname \_begincsname#2\_endcsname \_else \_undefined \_fi +} + +% Make \sdef public, so users can customize without messing with catcodes. +\_public \sdef ; + +\_def \_cs #1{\_csname#1\_endcsname} +\_def \_trycs#1#2{\_ifcsname #1\_endcsname \_csname #1\_ea\_endcsname \_else #2\_fi} + +\_long\_def \_addto #1#2{\_ea\_def\_ea#1\_ea{#1#2}} + +\_private \global \advance ; + +\_def\_incr #1{\_global\_advance#1by1 } +\_def\_decr #1{\_global\_advance#1by-1 } + +\_private \immediate \closeout \jobname ; + +% modified +\_def\_byehook{% + \_immediate\_closeout\_reffile + \_edef\_tmp{\_mdfive{\_jobname.ref}}% + \_ifx\_tmp\_prevrefhash\_else \_opwarning{Try to rerun, + \_jobname.ref file was \_ifx\_prevrefhash\_empty created\_else changed\_fi}\_fi +} + +% +% alloc.opm +% + +\_private \newcount ; + +% +% if-macros.opm +% + +\_def\_afterfi#1#2\_fi{\_fi#1} + +\_private \unless ; + +\_def\_isdefined #1#2{\_ifcsname #1\_endcsname \_else \_ea\_unless \_fi #2} + +\_private \newread \openin \relax \ifeof \closein ; + +\_newread \_testin +\_def\_isfile #1{% + \_immediate\_openin\_testin ={#1}\_relax + \_ifeof\_testin \_ea\_unless + \_else \_immediate\_closein\_testin + \_fi +} + +\_private \begingroup \toks \endgroup \futurelet ; + +\_long\_def\_isnextchar#1#2#3{\_begingroup\_toks0={\_endgroup#2}\_toks1={\_endgroup#3}% + \_let\_tmp= #1\_futurelet\_next\_isnextcharA +} +\_def\_isnextcharA{\_the\_toks\_ifx\_tmp\_next0\_else1\_fi\_space} + +% +% more-macros.opm +% + +\_private \string \newtoks \romannumeral ; + +\_def\_optdef#1[#2]{% + \_def#1{\_opt={#2}\_isnextchar[{\_cs{_oA:\_string#1}}{\_cs{_oB:\_string#1}}}% + \_sdef{_oA:\_string#1}[##1]{\_opt={##1}\_cs{_oB:\_string#1\_nospaceafter}}% + \_sdef{_oB:\_string#1\_nospaceafter}% +} +\_def\_nospaceafter#1{\_ea#1\_romannumeral-`\.} +\_newtoks\_opt + +\_private \noexpand \catcode \toksapp ; + +\_newtoks\_tmptoks +% modified +\_edef\_tmp{\_noexpand\_catcode`!=\_the\_catcode`! \_noexpand\_catcode`?=\_the\_catcode`?} +\_catcode`!=3 \_catcode`?=3 +\_def\_replstring #1#2#3{% \replstring #1{stringA}{stringB} + \_long\_def\_replacestringsA##1#2{\_tmptoks{##1}\_replacestringsB}% + \_long\_def\_replacestringsB##1#2{\_ifx!##1\_relax \_else \_toksapp\_tmptoks{#3##1}% + \_ea\_replacestringsB\_fi}% + \_ea\_replacestringsA #1?#2!#2% + \_long\_def\_replacestringsA##1?{\_tmptoks{##1}\_edef#1{\_the\_tmptoks}}% + \_ea\_replacestringsA \_the\_tmptoks} +\_tmp % modified + +% modified (no \_skiptoeol) +\_long\_def\_doc #1\_cod{} + +% +% keyval.opm +% + +\_def\_readkv#1{\_ea\_def\_ea\_tmpb\_ea{#1}% + \_replstring\_tmpb{= }{=}\_replstring\_tmpb{ =}{=}% + \_replstring\_tmpb{, }{,}\_replstring\_tmpb{,,}{,}% + \_ea \_kvscan \_tmpb,,=,} +\_def\_kvscan #1#2=#3,{\_ifx#1,\_else \_sdef{_kv:#1#2}{#3}\_ea\_kvscan\_fi} +\_def\_kv#1{\_trycs{_kv:#1}{\_kvunknown}} +\_def\_kvunknown{???} + +% +% ref-file.opm +% + +\_private \newwrite \write \iftrue \iffalse \input ; + +% modified +\_newwrite\_reffile +\_def\_inputref {% + \_isfile{\_jobname.ref}\_iftrue + \_edef\_prevrefhash{\_mdfive{\_jobname.ref}}% + \_input {\_jobname.ref}% + \_openref + \_fi +} + +\_private \luaescapestring ; + +% modified +\_def\_mdfive#1{\_directlua{ + local fh = io.open("\_luaescapestring{#1}", "rb") + if fh then + local data = fh:read("*a") + fh:close() + tex.print(md5.sumhexa(data)) + end +}} +\_def\_prevrefhash{} + +\_def\_wrefrelax#1#2{} +\_let\_wref=\_wrefrelax + +\_private \openout ; + +% modified +\_def\_openref {% + \_immediate\_openout\_reffile={\_jobname.ref}\_relax + \_gdef\_wref ##1##2{\_write\_reffile{\_bslash\_csstring##1##2}}% + \_immediate\_write\_reffile {\_pcent\_pcent\_space pdfextra - REF file}% + \_gdef\_openref{}% +} +\_def\_ewref #1#2{\_edef\_ewrefA{#2}\_ea\_wref\_ea#1\_ea{\_ewrefA}} + +\_private \ifnum ; + +\_def\_refdecl{\_bgroup \_catcode`\#=12 \_refdeclA} +\_def\_refdeclA #1{\_egroup\_openref + \_immediate\_write\_reffile {\_pcent\_space \_string \refdecl:}% + \_immediate\_write\_reffile {\_detokenize{#1}}% +} + +% +% hyperlinks.opm +% + +\_def\_pdfborder#1{\_ifcsname _#1border\_endcsname + /C [\_csname _#1border\_endcsname] /Border [0 0 .6]\_else /Border [0 0 0]\_fi +} + +% modified +\_def\_hyperlinks#1#2{% + \_let\_ilinkcolor=#1\_empty + \_let\_elinkcolor=#2\_empty +} +\_public \hyperlinks ; + +% +% pdfuni-string.opm +% + +% modified +\_def\_pdfunidef#1#2{% + \_edef\_tmp{#2}% + \_def#1{\_directlua{ + local str = token.scan_string() + local out = { "<FEFF" } + for _, c in utf8.codes(str) do + if c < 0x10000 then + out[\_csstring\#out+1] = string.format("\_pcent04X", c) + else + c = c - 0x10000 + local high = c >> 10 + 0xD800 + local low = c \_csstring\& 0x3FF + 0xDC00 + out[\_csstring\#out+1] = string.format("\_pcent04X\_pcent04X", high, low) + end + end + out[\_csstring\#out+1] = ">" + tex.print(table.concat(out, ""))}\_ea{\_tmp}% +}} + +% +% optex.lua +% + +\_private \setbox ; + +% modified +\_def\_preshipout#1#2{\_setbox#1#2} + +% +% luatex-ini.opm +% + +\_private \protected \pdfextension \pdffeedback \pdfvariable ; +\_private \numexpr \saveboxresource \lastsavedboxresourceindex ; + +\_protected\_def \_pdfobj {\_pdfextension obj } +\_protected\_def \_pdfrefobj {\_pdfextension refobj } +\_protected\_def \_pdflastobj {\_numexpr\_pdffeedback lastobj\_relax} +\_protected\_def \_pdfstartlink {\_pdfextension startlink } +\_protected\_def \_pdfendlink {\_pdfextension endlink\_relax} +\_protected\_def \_pdfcatalog {\_pdfextension catalog } +\_protected\_edef\_pdfpageattr {\_pdfvariable pageattr} +\_protected\_def \_pdfnames {\_pdfextension names } +\_protected\_def \_pdfannot {\_pdfextension annot } +\_protected\_def \_pdflastannot {\_numexpr\_pdffeedback lastannot\_relax} + \_def \_pdfpageref {\_pdffeedback pageref} + \_let \_pdfxform \_saveboxresource + \_let \_pdflastxform \_lastsavedboxresourceindex + +% +% output.opm +% + +\_let\_oldshipout=\shipout +\_def\shipout{\_begoutput\_oldshipout} + +% modified +\_newcount\_gpageno +\_def \_begoutput{\_incr\_gpageno + % we try \folio, or nothing ({}), pdfextra only uses \gpageno + \_immediate\_wref\_Xpage{{\_the\_gpageno}{\_trycs{folio}{}}}% +} + +% +% references.opm +% + +% modified +\_def\_Xpage#1#2{\_def\_currpage{{#1}{#2}}} + +% +% colors.opm +% + +% modified +\_let\_localcolor\_relax + +% +% \_byehook and \_inputref +% + +\_private \ifdefined ; + +\_ifdefined\AtEndDocument + % make sure all pages (and write whatsits) are flushed before we close files + \AtEndDocument{\clearpage\_byehook}% +\_else + % hope that usual \bye (\outer\def\bye{\par\vfill\supereject\end}) is used + \_let\_end\end + \_def\end{\_byehook \_end}% +\_fi + +\_inputref + +% continue with the real macros +\_input pdfextra.opm +\_catcode`\_=\pdfextraloaded |