summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/media9
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/media9
Initial commit
Diffstat (limited to 'macros/latex/contrib/media9')
-rw-r--r--macros/latex/contrib/media9/ChangeLog520
-rw-r--r--macros/latex/contrib/media9/README.md49
-rw-r--r--macros/latex/contrib/media9/doc/files/3dsystem.fig273
-rw-r--r--macros/latex/contrib/media9/doc/files/3dsystem.pdfbin0 -> 5989 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/3dsystem.tex29
-rw-r--r--macros/latex/contrib/media9/doc/files/bird.mp3bin0 -> 735130 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/boutona.pdfbin0 -> 2363 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/boutonb.pdfbin0 -> 2203 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/boutonc.pdfbin0 -> 2364 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/boutond.pdfbin0 -> 2199 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/boutone.pdfbin0 -> 2236 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/boutonf.pdfbin0 -> 2208 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/config.xml73
-rw-r--r--macros/latex/contrib/media9/doc/files/ctan_lion_1200.pngbin0 -> 321886 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/ctan_lion_200.pngbin0 -> 14220 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/ctan_lion_400.pngbin0 -> 44332 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/ctan_lion_800.pngbin0 -> 152532 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/cube.asy46
-rw-r--r--macros/latex/contrib/media9/doc/files/cube.mp4bin0 -> 262653 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/cubeposter.pngbin0 -> 3462 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/dice.u3dbin0 -> 160672 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/dice.vws26
-rw-r--r--macros/latex/contrib/media9/doc/files/dice.wrl119
-rw-r--r--macros/latex/contrib/media9/doc/files/epix.asy22
-rw-r--r--macros/latex/contrib/media9/doc/files/epix.prcbin0 -> 55043 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/epixposter.pdfbin0 -> 111371 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/malte.js42
-rw-r--r--macros/latex/contrib/media9/doc/files/malte.u3dbin0 -> 105696 bytes
-rw-r--r--macros/latex/contrib/media9/doc/files/random.mp4bin0 -> 20206 bytes
-rw-r--r--macros/latex/contrib/media9/doc/media9.pdfbin0 -> 3748849 bytes
-rw-r--r--macros/latex/contrib/media9/doc/media9.tex1284
-rw-r--r--macros/latex/contrib/media9/javascript/3Dmenu.js854
-rw-r--r--macros/latex/contrib/media9/javascript/3Dspintool.js29
-rw-r--r--macros/latex/contrib/media9/javascript/animation.js116
-rw-r--r--macros/latex/contrib/media9/javascript/asylabels.js85
-rw-r--r--macros/latex/contrib/media9/media9.sty2385
-rw-r--r--macros/latex/contrib/media9/pdfbase.sty1412
-rw-r--r--macros/latex/contrib/media9/players/APlayer.mxml429
-rw-r--r--macros/latex/contrib/media9/players/APlayer.swfbin0 -> 354878 bytes
-rw-r--r--macros/latex/contrib/media9/players/APlayer9.mxml417
-rw-r--r--macros/latex/contrib/media9/players/APlayer9.swfbin0 -> 206165 bytes
-rw-r--r--macros/latex/contrib/media9/players/BSD-License32
-rw-r--r--macros/latex/contrib/media9/players/SMPfixes.patch158
-rw-r--r--macros/latex/contrib/media9/players/SlideShow.mxml482
-rw-r--r--macros/latex/contrib/media9/players/SlideShow.swfbin0 -> 326715 bytes
-rw-r--r--macros/latex/contrib/media9/players/StrobeMediaPlayback-license5
-rw-r--r--macros/latex/contrib/media9/players/StrobeMediaPlayback.swfbin0 -> 275747 bytes
-rw-r--r--macros/latex/contrib/media9/players/VPlayer.mxml338
-rw-r--r--macros/latex/contrib/media9/players/VPlayer.swfbin0 -> 483412 bytes
-rw-r--r--macros/latex/contrib/media9/players/VPlayer9.mxml318
-rw-r--r--macros/latex/contrib/media9/players/VPlayer9.swfbin0 -> 219242 bytes
51 files changed, 9543 insertions, 0 deletions
diff --git a/macros/latex/contrib/media9/ChangeLog b/macros/latex/contrib/media9/ChangeLog
new file mode 100644
index 0000000000..fe6b6348a5
--- /dev/null
+++ b/macros/latex/contrib/media9/ChangeLog
@@ -0,0 +1,520 @@
+2019-07-11
+ * v1.02 : media9.sty
+ * v0.33 : pdfbase.sty
+ * fix : dvipdfmx warning about off-page annotation, occuring if
+ ocgbase.sty is loaded at the same time
+
+2019-05-24
+ * v1.01 : media9.sty
+ * v0.32 : pdfbase.sty
+ * fix : pdfbase: \pbs_pdfdest:nnnn (luaTeX version)
+
+2019-05-17
+ * v1.0 : media9.sty
+ * v0.31 : pdfbase.sty
+ * fix : pdfbase: missing expansion when writing label to
+ aux file (PDF dest, dvips)
+
+2019-05-15
+ * v0.99 : media9.sty
+ * v0.30 : pdfbase.sty
+ * fix : pdfbase, dvipdfmx: missing space after dim expression in
+ \pbs_pdfxform:nnnnn
+
+2019-05-10
+ * v0.98 : media9.sty
+ * v0.29 : pdfbase.sty
+ * fix : deprecated expl3 commands replaced
+
+2019-05-02
+ * v0.97 : media9.sty
+ * v0.28 : pdfbase.sty
+ * new : pdfbase.sty: PDF Link destinations (doc level navigation)
+
+2019-02-21
+ * v0.96 : media9.sty
+ * v0.27 : pdfbase.sty
+ * fix : pdfbase.sty: XeLaTeX run failure
+
+2019-02-19
+ * v0.95 : media9.sty
+ * v0.26 : pdfbase.sty
+ * fix : media9.sty: box scaling fixed after recent expl3 update
+ * new: pdfbase.sty: basic dvisvgm support
+
+2019-01-02
+ * v0.94 : media9.sty
+ * v0.25 : pdfbase.sty
+ * fix : merge request (latex3 end-2018 deprecations)
+
+2018-06-07
+ * v0.93 : media9.sty
+ * v0.24 : pdfbase.sty
+ * fix : pdfbase.sty: many cs now def'ed as `protected'; adjustments after
+ migration to GitLab
+
+2018-04-06
+ * v0.92 : media9.sty
+ * v0.23 : pdfbase.sty
+ * fix : pdfbase.sty: nesting level counter for `\pbs_pdfxform:nnnnn' in dvips mode
+
+2018-03-29
+ * v0.91 : media9.sty
+ * v0.22 : pdfbase.sty
+ * fix : pdfbase.sty: allow nested `\pbs_pdfxform:nnnnn' in dvips mode
+
+2018-02-28
+ * v0.90 : media9.sty
+ * v0.21 : pdfbase.sty
+ * fix : pdfbase.sty: `?pdfmark' code delayed to begin of document
+ * new: pdfbase.sty: adding \pbs_add_form_font: and \pbs_last_form_font: for
+ using embedded fonts in PDF Forms
+
+2018-01-18
+ * v0.89 : media9.sty
+ * v0.20 : pdfbase.sty
+ * fix : pdfbase.sty: end-of-dvi improved; VPlayer.mxml: `autoPlay' issue fixed
+
+2018-01-16
+ * v0.88 : media9.sty
+ * v0.19 : pdfbase.sty
+ * fix : pdfbase.sty: improving \pbs_at_end_dvi:n using `atbegshi' package
+
+2018-01-04
+ * v0.87 : media9.sty
+ * fix : media9.sty: fixing `kernel/invalid-variant' warnings
+
+2017-09-29
+ * v0.86 : media9.sty
+ * v0.18 : pdfbase.sty
+ * fix : pdfbase.sty: improving \pbs_pdfxform:nnnnn for dvips
+ * new: pdfbase.sty: \pbs_literal:nn (raw PDF/Postscript insertion)
+
+2017-08-25
+ * v0.85 : media9.sty
+ * v0.17 : pdfbase.sty
+ * fix : fixing output routine in pdfbase.sty: small vertical offset of page
+ content when loading package ocgx2
+
+2017-08-23
+ * v0.84 : media9.sty
+ * v0.16 : pdfbase.sty
+ * fix : output routine adjustments (pdfbase.sty)
+
+2017-07-27
+ * v0.83 : media9.sty
+ * fix : non-functional \mediabutton (broken in v. 0.81)
+
+2017-07-25
+ * v0.82 : media9.sty
+ * fix : deprecated expl3 function \file_add_path:nN (two occurrences) replaced
+ with \file_get_full_name:nN
+
+2017-07-24
+ * v0.81 : media9.sty
+ * v0.15 : pdfbase.sty
+ * fix : replace some \bool_if:n(TF) because of their new greedy behaviour
+ implemented in expl3
+
+2017-06-12
+ * v0.80 : media9.sty
+ * fix : obsolete l3regex warning
+
+2017-04-25
+ * v0.79 : media9.sty
+ * v0.14 : pdfbase.sty
+ * fix: improved A-Reader-related workaround for annotations/links
+ located on OCGs
+
+2017-03-23
+ * v0.78 : media9.sty
+ * v0.13 : pdfbase.sty
+ * fix: ocg-related improvements
+
+2017-03-16
+ * v0.77 : media9.sty
+ * v0.12 : pdfbase.sty
+ * fix: PDF annotation and Form XObject producing macros improved
+
+2017-02-08
+ * v0.76 : media9.sty
+ * fix: error in the caption of Fig. 8, documentation `media9.pdf' rebuilt
+
+2017-02-03
+ * v0.75 : media9.sty
+ * fix: RTL related (luaTeX)
+
+2017-01-30
+ * v0.74 : media9.sty
+ * fix: usability in righ-to-left typesetting context
+
+2017-01-24
+ * v0.73 : media9.sty
+ * v0.13 : pdfbase.sty
+ * fix: pdfbase.sty: xform generation via dvips: rescale oversized boxes to
+ fit within papersize to prevent cropping
+
+2017-01-10
+ * v0.72 : media9.sty
+ * v0.12 : pdfbase.sty
+ * fix: pdfbase.sty, media9.sty: `landscape' environment:
+ drivers XeTeX and dvipdfmx also supported
+
+2017-01-09
+ * v0.71 : media9.sty
+ * v0.11 : pdfbase.sty
+ * fix: pdfbase.sty: support for `landscape' environment of
+ the pdflscape/lscape packages
+
+2016-11-08
+ * v0.70 : media9.sty
+ * v0.10 : pdfbase.sty
+ * fix: pdfbase.sty & pdftex: sometimes, incorrect page ressources
+ for marked content were written when using \pbs_pdfbdc:nn;
+ reported by _FrnchFrgg_
+
+2016-06-08
+ * v0.69 : media9.sty
+ * v0.9 : pdfbase.sty
+ * fix: incompatibility with LuaTeX 0.95.0
+
+2016-03-22
+ * v0.68 : media9.sty
+ * v0.8 : pdfbase.sty
+ * fix: no more dependent on `l3regex.sty'
+
+2016-03-11
+ * v0.67 : media9.sty
+ * v0.7 : pdfbase.sty
+ * fix: minor adjustments
+
+2016-03-07
+ * v0.66 : media9.sty
+ * v0.6 : pdfbase.sty
+ * fix: minor adjustments of pdfbase.sty
+
+2016-02-25
+ * v0.65 : media9.sty
+ * v0.5 : pdfbase.sty
+ * fix: fixing possible colour leaking of play button overlay
+ and of m-button faces; minor adjustments of pdfbase.sty
+
+2016-02-13
+ * v0.64 : media9.sty
+ * v0.4 : pdfbase.sty, with minor fixes
+ * fix: fixing \pdfliteral for BDC/EMC insertion
+
+2016-01-29
+ * v0.63 : media9.sty
+ * fix: adjusted `atendvi'-related warning message
+
+2016-01-12
+ * v0.62 : media9.sty
+ * fix : xr-hyper/zref-ralated error
+
+2015-11-26
+ * v0.61 : media9.sty, with minor fixes
+
+2015-11-23
+ * v0.3 : pdfbase.sty, with minor fixes
+
+2015-11-02
+ * v0.60 : media9.sty
+ * v0.2 : pdfbase.sty
+ * fix: improved compatibility with ocgbase.sty
+
+2015-10-28
+ * v0.59 : media9.sty
+ * v0.1 : pdfbase.sty
+ * new: code restructured (new file pdfbase.sty with commands
+ for low-level access to PDF features); now using new ocgbase.sty
+ from ocgx2
+
+2015-09-01
+ * fix: recompiled VPlayer, APlayer, SlideShow, StrobeMediaPlayback
+
+2015-08-18
+ * v0.58
+ * fix: bug in SlideShow (measuring player dimensions for dyn. images)
+
+2015-07-30
+ * v0.57
+ * fix: `autoPlay' option of VPlayer didn't work reliably
+
+2015-07-28
+ * v0.56
+ * fix: expl3[2015/07/14]-related adjustments
+
+2015-07-20
+ * v0.55
+ * fix: expl3-related adjustments
+
+2015-06-23
+ * v0.54
+ * fix: flashing of videos when played in a loop
+
+2015-06-03
+ * v0.53
+ * New: `SlideShow.swf' with support added for dynamic resolution images
+
+2015-05-11
+ * v0.52
+ * New: `SlideShow.swf' (image gallery viewer for embedded and remote
+ live or static image files in the PNG, JPEG and GIF formats); right-click
+ context menu added to players APlayer.swf, VPlayer.swf, SlideShow.swf
+
+2015-03-11
+ * v0.51
+ * Fix: compatibility with `fixocgx' package
+
+2015-02-24
+ * v0.50
+ * Fix: minor code changes; reorganized TDS packaging
+
+2015-01-21
+ * v0.49
+ * Fix: some more OCG related code changes
+
+2014-10-20
+ * v0.48
+ * fix: minor code change related to AcroForm and OCProperties catalog entries
+
+2014-09-23
+ * v0.47
+ * new: `width', `height', `totalheight', `keepaspectratio', `scale' added as
+ package options; option `3Drender' accepts optional opacity values with
+ `Transparent' and `TransparentWireframe' values; new entries in the CROSSECT
+ section of 3D views: OPACITY, VISIBLE, INTERSECTIONVISIBLE, PLANECOLOR,
+ INTERSECTIONCOLOR for adjusting clipping plane appearance
+
+2014-08-06
+ * v0.46
+ * fix: compilation failure in `tabular' environment; replacing deprecated l3
+ functions; wrong foreground/background binding of RM annots
+
+2014-06-25
+ * v0.45
+ * fix: transparency in media button faces drawn with TikZ/PGF didn't work when
+ using the dvipdfmx/xetex driver
+ * new: [AV]Player ActionScript methods `play' and `pause' take optional time
+ offsets, allowing for playing portions of the media file
+ * Change: `depth' option deprecated and ignored if used, added `totalheight'
+ and `keepaspectratio' options
+
+2014-06-04
+ * v0.44
+ * fix: prevent the play button from being printed to paper or exported to
+ other file formats
+ * new: global and command option `attachfiles'
+
+2014-03-25
+ * v0.43
+ * fix: bugs related to AcroForm and OCProperties catalog entries;
+ documenation
+
+2014-03-21
+ * v0.42
+ * change: refined look for the play button overlay
+ * fix: minor bugs
+
+2014-03-06
+ * v0.41
+ * new: poster text, if provided, also shown in draft mode (instead of draft
+ box)
+ * new: updated VPlayer with `autoRewind' setting via FlashVars
+
+2014-02-17
+ * v0.40
+ * new: play button visible in AR only
+ * new: updated APlayer and VPlayer with new exposed ActionScript functions
+ `currentTime', `duration', `playing', `muted'
+
+2014-02-06
+ * v0.39
+ * fix: improved compatibility with `animate' package
+
+2013-12-04
+ * v0.38
+ * fix: bugs in 3Dmenu
+
+2013-11-27
+ * v0.37
+ * fix: error in case of control sequence used in the last argument of
+ \includemedia
+
+2013-11-26
+ * v0.36
+ * fix: Postscript error with zero size media annotations
+ * change: updated 3Dmenu.js, transformation of mesh group nodes now possible
+
+2013-11-15
+ * v0.35
+ * new: play button overlay, can be disabled with new option `noplaybutton'
+ * change: updated media players
+
+2013-11-12
+ * v0.34
+ * fix: `bigfiles' option and really big files caused dvips error
+
+2013-11-04
+ * new: media player Flash components VPlayer9.swf, APlayer9.swf added
+ for compatibility with the Flash Player 9 plugin that is bundled with
+ Adobe Reader 9.4.1 for Linux
+
+2013-09-24
+ * v0.33
+ * fix: Adobe Reader prompting for "save changes" on closing
+
+2013-09-06
+ * v0.32
+ * change: `bigfiles' package option no longer requires `-dNOSAFER' with
+ ps2pdf.
+
+2013-08-26
+ * v0.31
+ * fix: expl3 related fix for correct 3D views file processing
+
+2013-08-16
+ * v0.30
+ * change: update to current expl3 version
+
+2013-07-19
+ * v0.29
+ * change: minor improvements of media buttons
+
+2013-06-07
+ * fix: VPlayer updated
+
+2013-06-06
+ * fix: APlayer & VPlayer updated, all players recompiled using
+ ApacheFlexSDK-4.9.1
+
+2013-06-03
+ * v0.28
+ * fix: updated APlayer.swf
+ * new: options `draft' and `final' for media bottons
+
+2013-03-27
+ * new: APlayer displays ID3 tags (artist, song, album), if available
+
+2013-03-20
+ * v0.26
+ * new: specifier for floating window position on screen, default is centred
+ * v0.27
+ * fix: package option `windowed' did not work
+
+2013-03-08
+ * v0.25
+ * change: VPlayer.swf with transparent background; floating media window
+ now centred
+
+2013-02-21
+ * v0.24
+ * change: option `url' now deprecated
+
+2013-02-12
+ * v0.23
+ * change: minor usability improvements
+
+2013-01-31
+ * v0.22
+ * new: user command \mediabutton for creating media control buttons
+
+2013-01-23
+ * v0.21
+ * fix: swf media inserted after a 3D object didn't work, reported by C.
+ Sturm
+
+2013-01-21
+ * v0.20
+ * new: `hideBar' option (FlashVar) for APlayer.swf
+
+2013-01-07
+ * v0.19
+ * fix: default window size calculation for floating media window
+
+2012-12-21
+ * v0.18
+ * new: playback in floating window; VPlayer.swf can be configured for
+ stepping mode
+
+2012-11-26
+ * v0.17
+ * change: updated APlayer.swf with transparent background; poster is now inserted
+ into the page content stream, not in the appearance dictionary
+
+2012-11-20
+ * v0.16
+ * fix: `transparent' option now working with Flash content, updated
+ StrobeMediaPlayback.swf (OSMF-2.0)
+
+2012-10-04
+ * v0.15
+ * fix: error in case of multiple use of `3Dviews' option
+
+2012-10-02
+ * v0.14
+ * new: keyboard control added to VPlayer.swf and APlayer.swf
+
+2012-09-12
+ * v0.13
+ * fix: cross section related bug in 3Dmenu.js
+ * fix: asylabels.js with improved label control (requires asymptote-2.17)
+
+2012-09-07
+ * v0.12
+ * new: command option `3Dpsob' and 3d views file entry `PSOB' added on
+ request of J. Bowman (Asymptote)
+ * fix: empty option list of \includemedia led to crash, improved Asymptote
+ compatibility
+
+2012-08-14
+ * v0.11
+ * new: package option `bigfiles' added which defers file embedding to the
+ ps->pdf step; needed when latex aborts with error `TeX capacity exceeded'
+ * fix: minor fixes in 3Dmenu.js and media9.sty
+
+2012-07-31
+ * v0.9, v.10
+ * fix: new l3fp infix syntax for calculations; deprecated expl3 functions
+ replaced
+
+2012-07-24
+ * v0.8
+ * fix: changed behaviour of \fp_use:N (l3fp package) caused failure;
+ opening 3D views file lead to error due to changed behaviour of
+ \ior_open:Nn (l3file package)
+
+2012-05-02
+ * v0.7
+ * new: 3Dpartsattrs added as package option
+
+2012-04-25
+ * v0.6
+ * new: command options added: 3Dpartsattrs, label;
+ updated APlayer and VPlayer versions with exposed ActionScript functions
+ for player control from JavaScript
+
+2012-04-16
+ * v0.5
+ * fix: option 3Dortho did not work, documentation update
+
+2012-04-12
+ * v0.4
+ * new: dependency on babel package removed; code contributed by
+ Enrico Gregorio
+ * fix: annotation box size calculation produced non-intuitive results
+
+2012-04-03
+ * v0.3
+ * fix: `deactivate' option did not work
+ * new: enhanced StrobeMediaPlayback.swf video player included
+
+2012-03-16
+ * v0.2
+ * new: LuaLaTeX support
+ * fix: wrong poster dimension in dvi output; fp numbers in PDF objects had
+ too many decimal places for dvipdfmx
+
+2012-03-01
+ * initial release (v0.1) to CTAN
diff --git a/macros/latex/contrib/media9/README.md b/macros/latex/contrib/media9/README.md
new file mode 100644
index 0000000000..17b5f71a77
--- /dev/null
+++ b/macros/latex/contrib/media9/README.md
@@ -0,0 +1,49 @@
+# The `media9` LaTeX Package
+
+© 2012--`\today` Alexander Grahn
+
+https://gitlab.com/agrahn/media9
+
+## Description
+
+This package provides an interface to embed interactive Flash (SWF) and 3D objects (Adobe U3D & PRC) as well as video and sound files or streams in the popular MP4, FLV, MP3 formats into PDF documents with Acrobat-9/X compatibility. Playback of multimedia files relies on Adobe Flash Player which supports the efficient H.264 codec for video compression.
+
+This package is based on the RichMedia Annotation, an Adobe addition to the PDF specification. It replaces the now obsolete `movie15` package.
+
+>Note that Adobe Reader for Linux has dropped Flash support since version 9.4.2.
+
+Supported PDF making workflows are:
+* pdfLaTeX, LuaLaTeX
+* (Lua)LaTeX &rArr; dvips &rArr; ps2pdf/Distiller
+* (Xe)LaTeX &rArr; (x)dvipdfmx
+
+The package was written in the new LaTeX3 syntax.
+
+## Requirements
+
+Recent versions of
+* l3kernel (LaTeX package)
+* l3packages (LaTeX package)
+* Ghostscript or Adobe Distiller for PS to PDF conversion
+* dvipdfmx for DVI to PDF conversion
+* Adobe Reader
+
+## Installation
+
+MiKTeX and TeXLive users should run the package manager for installation.
+
+Otherwise, a manual install into the local TeX-Directory-Structure (TDS) root directory can be done along the following steps:
+
+1. Download the TDS compliant package file [`media9.tds.zip`](http://mirror.ctan.org/install/macros/latex/contrib/media9.tds.zip)
+
+2. Find the local TDS root directory by running `kpsewhich -var-value TEXMFLOCAL` on the command line.
+
+3. Unzip media9.tds.zip into the local TDS root directory previously found. Depending on the location of this directory, you may need to be logged in as Root/Administrator.
+
+4. After installation, update the filename database by running `texhash` on the command line. Again, Root/Administrator privileges may be required.
+
+For updating the package, repeat the steps given above.
+
+## License
+
+This material is subject to the [LaTeX Project Public License](http://mirrors.ctan.org/macros/latex/base/lppl.txt).
diff --git a/macros/latex/contrib/media9/doc/files/3dsystem.fig b/macros/latex/contrib/media9/doc/files/3dsystem.fig
new file mode 100644
index 0000000000..e2273139db
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/3dsystem.fig
@@ -0,0 +1,273 @@
+#FIG 3.2
+Landscape
+Center
+Metric
+A4
+100.00
+Single
+-2
+1200 2
+0 32 #9c0000
+0 33 #8c8c8c
+0 34 #8c8c8c
+0 35 #424242
+0 36 #8c8c8c
+0 37 #424242
+0 38 #8c8c8c
+0 39 #424242
+0 40 #8c8c8c
+0 41 #424242
+0 42 #8c8c8c
+0 43 #424242
+0 44 #e2e2ee
+0 45 #94949a
+0 46 #dbdbdb
+0 47 #a1a1b7
+0 48 #ededed
+0 49 #e0e0e0
+0 50 #86acff
+0 51 #7070ff
+0 52 #c6b797
+0 53 #eff8ff
+0 54 #dccba6
+0 55 #404040
+0 56 #808080
+0 57 #c0c0c0
+0 58 #8e8f8e
+0 59 #aaaaaa
+0 60 #555555
+0 61 #8e8e8e
+0 62 #d7d7d7
+0 63 #aeaeae
+0 64 #bebebe
+0 65 #515151
+0 66 #e7e3e7
+0 67 #000049
+0 68 #797979
+0 69 #303430
+0 70 #414141
+0 71 #c7b696
+0 72 #414541
+0 73 #e7e7e7
+0 74 #717571
+0 75 #dd9d93
+0 76 #f1ece0
+0 77 #c3c3c3
+0 78 #e2c8a8
+0 79 #e1e1e1
+0 80 #d2d2d2
+0 81 #da7a1a
+0 82 #f1e41a
+0 83 #887dc2
+0 84 #d6d6d6
+0 85 #8c8ca5
+0 86 #4a4a4a
+0 87 #8c6b6b
+0 88 #5a5a5a
+0 89 #636363
+0 90 #b79b73
+0 91 #4193ff
+0 92 #bf703b
+0 93 #db7700
+0 94 #dab800
+0 95 #006400
+0 96 #5a6b3b
+0 97 #d3d3d3
+0 98 #8e8ea4
+0 99 #f3b95d
+0 100 #89996b
+0 101 #646464
+0 102 #b7e6ff
+0 103 #86c0ec
+0 104 #bdbdbd
+0 105 #d39552
+0 106 #98d2fe
+0 107 #8c9c6b
+0 108 #f76b00
+0 109 #5a6b39
+0 110 #8c9c6b
+0 111 #8c9c7b
+0 112 #184a18
+0 113 #adadad
+0 114 #f7bd5a
+0 115 #636b9c
+0 116 #f7f7f7
+0 117 #de0000
+0 118 #adadad
+0 119 #f7bd5a
+0 120 #adadad
+0 121 #f7bd5a
+0 122 #636b9c
+0 123 #526b29
+0 124 #949494
+0 125 #006300
+0 126 #00634a
+0 127 #7b844a
+0 128 #e7bd7b
+0 129 #a5b5c6
+0 130 #6b6b94
+0 131 #846b6b
+0 132 #529c4a
+0 133 #d6e7e7
+0 134 #526363
+0 135 #186b4a
+0 136 #9ca5b5
+0 137 #ff9400
+0 138 #ff9400
+0 139 #00634a
+0 140 #7b844a
+0 141 #63737b
+0 142 #e7bd7b
+0 143 #184a18
+0 144 #f7bd5a
+0 145 #dedede
+0 146 #f3eed3
+0 147 #f5ae5d
+0 148 #95ce99
+0 149 #b5157d
+0 150 #eeeeee
+0 151 #848484
+0 152 #7b7b7b
+0 153 #005a00
+0 154 #e77373
+0 155 #ffcb31
+0 156 #29794a
+0 157 #de2821
+0 158 #2159c6
+0 159 #f8f8f8
+0 160 #e6e6e6
+0 161 #21845a
+0 162 #cccccc
+0 163 #999999
+0 164 #b2b2b2
+0 165 #c9c9c9
+0 166 #dfd8df
+0 167 #f7f3f7
+0 168 #cdcdcd
+0 169 #6c6c6c
+5 1 0 1 0 7 45 -1 -1 0.000 0 0 1 1 1543.224 2237.643 2833 2393 2598 2996 2143 3390
+ 2 0 1.00 60.00 120.00
+ 2 0 1.00 60.00 120.00
+5 1 2 1 0 7 52 -1 -1 4.000 1 0 1 1 11576.552 9623.869 442 3442 1349 2035 2462 729
+ 0 0 1.00 60.00 120.00
+ 0 0 1.00 60.00 120.00
+5 1 2 1 0 7 52 -1 -1 4.000 1 1 1 1 7430.119 -1873.223 802 1109 1315 2055 2088 3055
+ 0 0 1.00 60.00 120.00
+ 0 0 1.00 60.00 120.00
+6 4753 4744 6037 5510
+5 1 0 1 0 7 100 -1 -1 0.000 0 0 0 0 5712.721 5265.408 5756 4952 5978 5093 6024 5322
+5 1 2 1 0 7 100 -1 -1 2.000 1 0 0 0 5442.537 5285.331 4944 5096 5258 4785 5692 4814
+5 1 2 1 0 7 100 -1 -1 2.000 1 1 0 0 5841.704 5281.825 5968 5335 5808 5149 5709 5316
+5 1 2 1 0 7 100 -1 -1 2.000 1 0 0 0 5122.611 5132.487 4986 5129 5207 5025 5230 5217
+5 1 0 1 0 7 98 -1 -1 0.000 1 0 0 0 5468.833 4766.333 5072 5236 5056 5222 5039 5206
+5 1 0 1 0 7 98 -1 -1 0.000 1 1 0 0 5071.511 5140.272 5011 5132 5012 5154 5026 5181
+5 1 0 1 0 7 98 -1 -1 0.000 1 0 0 0 5945.000 5164.000 5794 5382 5778 5370 5760 5354
+5 1 0 1 0 7 50 -1 -1 0.000 1 1 0 0 5659.846 5403.315 5786 5455 5627 5271 5528 5438
+5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 5260.632 5406.447 4760 5215 5074 4904 5510 4932
+5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 5529.852 5383.891 5574 5071 5795 5212 5841 5439
+5 1 0 1 0 7 50 -1 -1 0.000 1 0 0 0 4941.237 5252.972 4804 5250 5026 5145 5049 5338
+5 1 0 1 0 7 98 -1 -1 0.000 1 0 0 0 5442.380 5294.428 5068 4904 5255 4787 5689 4813
+5 1 0 1 0 7 98 -1 -1 0.000 1 1 0 0 5789.373 5293.415 5732 5285 5733 5307 5747 5333
+5 1 0 1 0 7 98 -1 -1 0.000 1 1 0 0 5825.000 5279.500 5704 5271 5704 5288 5706 5303
+1 3 2 1 0 7 100 -1 -1 2.000 1 6.0772 5123 5130 111 111 5123 5130 5234 5130
+1 3 2 1 0 7 100 -1 -1 2.000 1 6.0772 5844 5281 111 111 5844 5281 5955 5281
+1 3 0 1 0 7 50 -1 -1 0.000 1 6.0772 4940 5254 111 111 4940 5254 5050 5274
+1 3 0 1 0 7 50 -1 -1 0.000 1 6.0772 5662 5402 111 111 5662 5402 5703 5507
+2 1 0 1 0 7 100 -1 -1 0.000 0 0 -1 0 0 2
+ 5692 4814 5756 4952
+2 1 2 1 0 7 100 -1 -1 2.000 0 1 -1 0 0 2
+ 6024 5320 5968 5335
+2 1 2 1 0 7 100 -1 -1 2.000 0 1 -1 0 0 2
+ 4941 5094 4986 5129
+2 1 2 1 0 7 100 -1 -1 2.000 0 1 -1 0 0 2
+ 4761 5215 4944 5096
+2 1 2 1 0 7 98 -1 -1 2.000 0 1 -1 0 0 2
+ 5049 5338 5228 5220
+2 1 2 1 0 7 98 -1 -1 2.000 0 1 -1 0 0 2
+ 5528 5438 5709 5319
+2 1 2 1 0 7 98 -1 -1 2.000 0 1 -1 0 0 2
+ 4806 5250 4989 5131
+2 1 2 1 0 7 98 -1 -1 2.000 0 1 -1 0 0 2
+ 5786 5454 5968 5336
+2 1 0 1 0 7 98 -1 -1 0.000 0 1 -1 0 0 2
+ 4953 5157 4992 5131
+2 1 0 1 0 7 80 -1 -1 0.000 0 0 -1 0 0 2
+ 4971 4954 5153 4835
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 4804 5251 4842 5227
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 4760 5215 4804 5250
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 5049 5338 5528 5438
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 5841 5439 5786 5455
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 5510 4932 5574 5071
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 5528 5438 5565 5413
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 5841 5439 6023 5322
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 5755 4952 5574 5071
+2 1 0 1 0 7 50 -1 -1 0.000 0 1 -1 0 0 2
+ 5690 4814 5510 4932
+2 1 2 1 0 7 100 -1 -1 2.000 0 1 -1 0 0 2
+ 5228 5217 5708 5316
+-6
+6 1009 1590 1714 2595
+6 1399 1782 1714 2435
+5 1 0 0 0 7 55 -1 2 0.000 0 0 0 0 1438.929 2167.804 1604 1949 1708 2220 1500 2435
+2 3 0 0 7 7 55 -1 2 0.000 1 0 -1 0 0 6
+ 1399 1782 1604 1948 1501 2435 1462 2428 1460 1899 1399 1782
+-6
+2 3 0 2 0 7 50 -1 14 0.000 1 0 -1 0 0 5
+ 1300 1607 1613 2067 1322 2533 1024 2058 1300 1607
+2 3 0 2 0 7 54 -1 14 0.000 1 0 -1 0 0 6
+ 1322 2534 1406 2580 1692 2125 1610 2073 1323 2535 1322 2534
+2 3 0 2 0 7 54 -1 10 0.000 1 0 -1 0 0 5
+ 1300 1605 1384 1668 1693 2117 1618 2072 1300 1605
+-6
+1 1 1 1 0 7 50 -1 -1 4.000 1 5.7596 3060 3330 405 900 3060 3330 3465 3337
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 1 3
+ 1 1 2.00 120.00 240.00
+ 1 1 2.00 120.00 240.00
+ 2205 1305 2205 6165 7245 7155
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 2205 6165 5985 4095
+2 1 1 1 4 7 50 -1 -1 4.000 0 1 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 5440 5973 5440 5112
+2 1 1 1 4 7 50 -1 -1 4.000 0 1 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 4331 6582 5440 5973
+2 1 0 2 4 7 50 -1 -1 0.000 0 0 -1 1 0 2
+ 2 0 2.00 120.00 240.00
+ 2205 6165 5440 5112
+2 1 1 1 1 7 50 -1 -1 4.000 0 1 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 4621 4954 4014 5283
+2 1 1 1 1 7 50 -1 -1 4.000 0 1 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 4014 5283 4014 4048
+2 1 0 2 1 7 60 -1 -1 0.000 0 0 -1 1 0 2
+ 2 0 2.00 120.00 240.00
+ 5441 5112 4014 4048
+2 1 1 1 1 7 60 -1 -1 4.000 0 1 -1 1 0 2
+ 2 0 1.00 66.00 130.50
+ 5441 5112 4622 4953
+2 1 1 1 4 7 49 -1 -1 4.000 0 1 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 2205 6165 4331 6582
+2 1 3 2 1 7 70 -1 -1 7.500 0 1 -1 0 0 2
+ 5443 5112 1683 2312
+2 1 1 1 0 7 100 -1 -1 4.000 0 0 -1 0 0 3
+ 3465 2532 1335 2060 2532 4025
+4 0 1 50 -1 0 12 0.0000 3 195 1845 4470 4308 $\\overrightarrow{C2C}$\001
+4 0 0 50 -1 0 12 0.0000 3 165 315 6013 4272 $Y$\001
+4 0 0 50 -1 0 12 0.0000 3 165 315 7140 7372 $X$\001
+4 0 0 50 -1 0 12 0.0000 3 165 300 2266 1297 $Z$\001
+4 0 4 50 -1 0 12 0.0000 3 195 1905 4090 5815 $\\overrightarrow{COO}$\001
+4 2 0 50 -1 0 12 0.0000 3 195 690 2160 6210 $(0,0,0)$\001
+4 1 0 50 -1 0 12 0.0000 3 195 1755 2835 2310 $\\sphericalangle AAC$\001
+4 0 1 50 -1 0 12 0.0000 3 165 570 3635 3680 $ROO$\001
diff --git a/macros/latex/contrib/media9/doc/files/3dsystem.pdf b/macros/latex/contrib/media9/doc/files/3dsystem.pdf
new file mode 100644
index 0000000000..4d09d34fc4
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/3dsystem.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/3dsystem.tex b/macros/latex/contrib/media9/doc/files/3dsystem.tex
new file mode 100644
index 0000000000..6bd2ffa196
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/3dsystem.tex
@@ -0,0 +1,29 @@
+\begin{picture}(0,0)%
+\includegraphics[clip, scale=1, viewport=0 0 435 423.334371108344]{3dsystem}%
+\end{picture}%
+\setlength{\unitlength}{4144sp}%
+%
+\begingroup\makeatletter\ifx\SetFigFont\undefined%
+\gdef\SetFigFont#1#2#3#4#5{%
+ \reset@font\fontsize{#1}{#2pt}%
+ \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
+ \selectfont}%
+\fi\endgroup%
+\begin{picture}(6905.17992277992,6720)(435,-6602)%
+\put(4471,-3469){\makebox(0,0)[lb]{\smash{{\SetFigFont{12}{14.4}{\familydefault}{\mddefault}{\updefault}{\color[rgb]{0,0,1}$\overrightarrow{C2C}$}%
+}}}}%
+\put(6014,-3433){\makebox(0,0)[lb]{\smash{{\SetFigFont{12}{14.4}{\familydefault}{\mddefault}{\updefault}{\color[rgb]{0,0,0}$Y$}%
+}}}}%
+\put(7141,-6533){\makebox(0,0)[lb]{\smash{{\SetFigFont{12}{14.4}{\familydefault}{\mddefault}{\updefault}{\color[rgb]{0,0,0}$X$}%
+}}}}%
+\put(2267,-458){\makebox(0,0)[lb]{\smash{{\SetFigFont{12}{14.4}{\familydefault}{\mddefault}{\updefault}{\color[rgb]{0,0,0}$Z$}%
+}}}}%
+\put(4091,-4976){\makebox(0,0)[lb]{\smash{{\SetFigFont{12}{14.4}{\familydefault}{\mddefault}{\updefault}{\color[rgb]{1,0,0}$\overrightarrow{COO}$}%
+}}}}%
+\put(2161,-5371){\makebox(0,0)[rb]{\smash{{\SetFigFont{12}{14.4}{\familydefault}{\mddefault}{\updefault}{\color[rgb]{0,0,0}$(0,0,0)$}%
+}}}}%
+\put(2836,-1471){\makebox(0,0)[b]{\smash{{\SetFigFont{12}{14.4}{\familydefault}{\mddefault}{\updefault}{\color[rgb]{0,0,0}$\sphericalangle AAC$}%
+}}}}%
+\put(3636,-2841){\makebox(0,0)[lb]{\smash{{\SetFigFont{12}{14.4}{\familydefault}{\mddefault}{\updefault}{\color[rgb]{0,0,1}$ROO$}%
+}}}}%
+\end{picture}%
diff --git a/macros/latex/contrib/media9/doc/files/bird.mp3 b/macros/latex/contrib/media9/doc/files/bird.mp3
new file mode 100644
index 0000000000..c9bee679cd
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/bird.mp3
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/boutona.pdf b/macros/latex/contrib/media9/doc/files/boutona.pdf
new file mode 100644
index 0000000000..7ba15d820d
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/boutona.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/boutonb.pdf b/macros/latex/contrib/media9/doc/files/boutonb.pdf
new file mode 100644
index 0000000000..605076e407
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/boutonb.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/boutonc.pdf b/macros/latex/contrib/media9/doc/files/boutonc.pdf
new file mode 100644
index 0000000000..adf016f955
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/boutonc.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/boutond.pdf b/macros/latex/contrib/media9/doc/files/boutond.pdf
new file mode 100644
index 0000000000..a8170efa61
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/boutond.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/boutone.pdf b/macros/latex/contrib/media9/doc/files/boutone.pdf
new file mode 100644
index 0000000000..a87b38d6fd
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/boutone.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/boutonf.pdf b/macros/latex/contrib/media9/doc/files/boutonf.pdf
new file mode 100644
index 0000000000..b687d0aa2e
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/boutonf.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/config.xml b/macros/latex/contrib/media9/doc/files/config.xml
new file mode 100644
index 0000000000..4da63bba34
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/config.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<SlideShow>
+ <!--
+ Configuration file template for SlideShow.swf
+
+ SlideShow.swf can display life and static PNG/JPEG/GIF image files from
+ remote servers or residing on the same server as the app itself.
+
+ Pass this file to SlideShow.swf using the `xml=<configuration file>'
+ FlashVar.
+
+ **NOTE**: For security reasons of Flash Player, the configuration file and
+ SlideShow.swf must reside on the same web server or be both embedded into
+ the PDF. Different web servers or the combination of web server location
+ and embedding into the PDF throws a security error.
+
+ An image definition starts with <Img [attributes]> and ends with </Img>.
+ All attributes in the opening <Img [attributes]> tag are optional:
+
+ live="<refresh interval in [s]>" for live remote image
+ rot90="<integer number>" initial rotation by number*90°
+
+ Inside an image definition, at least one <URL [attributes]>...</URL>
+ element is required. The attributes are optional. More than one URL may be
+ given to provide image files at various resolutions. The best one fitting
+ within the current display size will be chosen dynamically. In the case of
+ multiple URLs, the actual resolution of each image should be told using the
+
+ size="<width>x<height>"
+
+ attribute, which specifies the image file dimensions in pixels.
+
+ Some servers generate bitmap graphics at arbitrary resolution upon request.
+ In such cases, one or both of the attributes
+
+ width="<width pattern>" height="<height pattern>"
+
+ may be given in the opening URL tag. <width pattern> and <height pattern>,
+ if present in the URL string, will be substituted with the current display
+ dimensions.
+
+ An image URL, given between <URL> and </URL>, can be absolute, that is,
+ starting with 'http://...', or relative to the location of SlideShow.swf.
+
+ The <caption>...</caption> element inside the image definition is optional.
+ -->
+ <Img live="3"> <!-- Live image, refreshed every 3 seconds -->
+ <caption>Mow-the-lawn simulation (live remote image).</caption>
+ <URL>http://1.618034.com/corner-icon.png</URL>
+ </Img>
+ <Img> <!-- remote image with dynamic size URL -->
+ <caption>"An algorithm must be seen to be believed." (Donald E. Knuth)
+Remote image URL with dynamic size that is reloaded while resizing the display.</caption>
+ <URL width="@@@@@">https://openclipart.org/image/@@@@@px/svg_to_png/137407/1304882618.png</URL>
+ </Img>
+ <Img> <!-- remote image with multiple URLs of different resolution -->
+ <caption>CTAN lion drawing by Duane Bibby. Thanks to www.ctan.org.
+Remote image with URLs at various resolutions ("responsive image").</caption>
+ <URL size="200x178" >
+ http://mirrors.ibiblio.org/CTAN/macros/latex/contrib/media9/doc/files/ctan_lion_200.png</URL>
+ <URL size="400x355" >
+ http://mirrors.ibiblio.org/CTAN/macros/latex/contrib/media9/doc/files/ctan_lion_400.png</URL>
+ <URL size="800x710" >
+ http://mirrors.ibiblio.org/CTAN/macros/latex/contrib/media9/doc/files/ctan_lion_800.png</URL>
+ <URL size="1200x1065">
+ http://mirrors.ibiblio.org/CTAN/macros/latex/contrib/media9/doc/files/ctan_lion_1200.png</URL>
+ </Img>
+ <Img rot90="3"> <!-- static image with URL relative to SlideShow.swf location -->
+ <caption>Static embedded image file with initial rotation of 3x90°.
+Press "r" or "R" to change orientation.</caption>
+ <URL>files/cubeposter.png</URL>
+ </Img>
+</SlideShow>
diff --git a/macros/latex/contrib/media9/doc/files/ctan_lion_1200.png b/macros/latex/contrib/media9/doc/files/ctan_lion_1200.png
new file mode 100644
index 0000000000..2350a76c2b
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/ctan_lion_1200.png
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/ctan_lion_200.png b/macros/latex/contrib/media9/doc/files/ctan_lion_200.png
new file mode 100644
index 0000000000..318c3f0a55
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/ctan_lion_200.png
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/ctan_lion_400.png b/macros/latex/contrib/media9/doc/files/ctan_lion_400.png
new file mode 100644
index 0000000000..167b806a02
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/ctan_lion_400.png
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/ctan_lion_800.png b/macros/latex/contrib/media9/doc/files/ctan_lion_800.png
new file mode 100644
index 0000000000..8090059600
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/ctan_lion_800.png
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/cube.asy b/macros/latex/contrib/media9/doc/files/cube.asy
new file mode 100644
index 0000000000..606375763d
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/cube.asy
@@ -0,0 +1,46 @@
+import math;
+import bsp;
+import animation;
+
+size(100,100);
+
+animation a;
+
+void face(face[] faces, path3 p, int j, int k) {
+ picture pic=faces.push(p);
+ filldraw(pic,project(p),Pen(j));
+ int sign=(k % 2 == 0) ? 1 : -1;
+ transform t=scale(4)*transform(dir(p,0,sign),dir(p,0,-sign));
+ label(pic,t*(string) j,project(0.5*(min(p)+max(p))));
+}
+
+void snapshot(transform3 t)
+{
+ static transform3 s=shift(-0.5*(X+Y+Z));
+ save();
+
+ face[] faces;
+ int j=-1;
+ transform3 T=t*s;
+ for(int k=0; k < 2; ++k) {
+ face(faces,T*plane((1,0,0),(0,1,0),(0,0,k)),++j,k);
+ face(faces,T*plane((0,1,0),(0,0,1),(k,0,0)),++j,k);
+ face(faces,T*plane((0,0,1),(1,0,0),(0,k,0)),++j,k);
+ }
+ add(faces);
+
+ a.add();
+ restore();
+}
+
+int n=50;
+
+real step=360/n;
+for(int i=0; i < n; ++i)
+ snapshot(rotate(i*step,X));
+for(int i=0; i < n; ++i)
+ snapshot(rotate(i*step,Y));
+for(int i=0; i < n; ++i)
+ snapshot(rotate(i*step,Z));
+
+a.movie(loops=1,delay=50);
diff --git a/macros/latex/contrib/media9/doc/files/cube.mp4 b/macros/latex/contrib/media9/doc/files/cube.mp4
new file mode 100644
index 0000000000..cbd115b211
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/cube.mp4
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/cubeposter.png b/macros/latex/contrib/media9/doc/files/cubeposter.png
new file mode 100644
index 0000000000..ae57869756
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/cubeposter.png
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/dice.u3d b/macros/latex/contrib/media9/doc/files/dice.u3d
new file mode 100644
index 0000000000..ae14b756a1
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/dice.u3d
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/dice.vws b/macros/latex/contrib/media9/doc/files/dice.vws
new file mode 100644
index 0000000000..ef77a276fe
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/dice.vws
@@ -0,0 +1,26 @@
+VIEW=Front
+ ROO=27
+END
+VIEW=Back
+ ROO=27
+ C2C=0 1 0
+END
+VIEW=Left
+ ROO=27
+ C2C=-1 0 0
+END
+VIEW=Right
+ ROO=27
+ C2C=1 0 0
+END
+VIEW=Top
+ ROO=27
+ C2C=0 0 1
+END
+VIEW=Bottom
+ ROO=27
+ C2C=0 0 -1
+END
+VIEW=Fish Eye at Centre
+ AAC=130
+END
diff --git a/macros/latex/contrib/media9/doc/files/dice.wrl b/macros/latex/contrib/media9/doc/files/dice.wrl
new file mode 100644
index 0000000000..23b1ef92a2
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/dice.wrl
@@ -0,0 +1,119 @@
+#VRML V1.0 ascii
+
+# dice.wrl
+# based on the work of
+# Peter Whitehouse <pwhitehouse@optusnet.com.au>
+# http://www.wonko.info/vrml/dice.txt
+
+Separator {
+ DEF _dice Shape {
+ geometry Box {
+ size 8 8 8
+ }
+ }
+ DEF _sphere0 Separator {
+ Material { diffuseColor [ 0.2 0.8 0.2 ] }
+ Translation { translation -2 2 -4 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_0 Separator {
+ Material { diffuseColor [ 0.2 0.8 0.2 ] }
+ Translation { translation 2 -2 -4 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1 Separator {
+ Material { diffuseColor [ 1 0.5 0.8 ] }
+ Translation { translation -2 2 4 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_0_1 Separator {
+ Material { diffuseColor [ 1 0.5 0.8 ] }
+ Translation { translation 2 -2 4 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2 Separator {
+ Material { diffuseColor [ 1 0.5 0.8 ] }
+ Translation { translation 0 0 4 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_3 Separator {
+ Material { diffuseColor [ 0.3 0.3 1 ] }
+ Translation { translation -4 2 1.5 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_0_1_3 Separator {
+ Material { diffuseColor [ 0.3 0.3 1 ] }
+ Translation { translation -4 2 -1.5 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_3_4 Separator {
+ Material { diffuseColor [ 0.3 0.3 1 ] }
+ Translation { translation -4 0 1.5 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_0_1_3_4 Separator {
+ Material { diffuseColor [ 0.3 0.3 1 ] }
+ Translation { translation -4 -2 -1.5 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_4 Separator {
+ Material { diffuseColor [ 0.3 0.3 1 ] }
+ Translation { translation -4 -2 1.5 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_6 Separator {
+ Material { diffuseColor [ 1 1 0 ] }
+ Translation { translation 0 4 0 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_7 Separator {
+ Material { diffuseColor [ 1 1 0 ] }
+ Translation { translation 2 4 -2 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_6_7 Separator {
+ Material { diffuseColor [ 1 1 0 ] }
+ Translation { translation 2 4 2 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_7_8 Separator {
+ Material { diffuseColor [ 1 0 0 ] }
+ Translation { translation -2 -4 -2 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_6_7_8 Separator {
+ Material { diffuseColor [ 1 0 0 ] }
+ Translation { translation -2 -4 2 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_7_9 Separator {
+ Material { diffuseColor [ 1 0 0 ] }
+ Translation { translation 2 -4 -2 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_6_7_9 Separator {
+ Material { diffuseColor [ 1 0 0 ] }
+ Translation { translation 2 -4 2 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_7_8_9 Separator {
+ Material { diffuseColor [ 1 1 0 ] }
+ Translation { translation -2 4 -2 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_2_3_5_6_7_8_9 Separator {
+ Material { diffuseColor [ 1 1 0 ] }
+ Translation { translation -2 4 2 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_3_10 Separator {
+ Material { diffuseColor [ 0 1 1 ] }
+ Translation { translation 4 0 0 }
+ Sphere { radius 1.00000 }
+ }
+ DEF _sphere0_1_3_4_12 Separator {
+ Material { diffuseColor [ 0.3 0.3 1 ] }
+ Translation { translation -4 0 -1.5 }
+ Sphere { radius 1.00000 }
+ }
+}
diff --git a/macros/latex/contrib/media9/doc/files/epix.asy b/macros/latex/contrib/media9/doc/files/epix.asy
new file mode 100644
index 0000000000..60720aaba0
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/epix.asy
@@ -0,0 +1,22 @@
+//Asymptote source file of epix.prc
+//
+//taken from http://asymptote.sourceforge.net/gallery/3D%20graphs/
+//and slightly modified
+//
+//run asy -k --tex=pdflatex on this file to generate standalone PRC
+
+import graph3;
+
+size(200,200,IgnoreAspect);
+
+currentprojection=perspective(4,2,3);
+//currentprojection=perspective(0,0,1);
+
+real f(pair z) {return z.y^3/2-3z.x^2*z.y;}
+
+draw(surface(f,(-1,-1),(1,1),nx=10,Spline),green,render(merge=true));
+draw(Label("$y$",1),(0,0,0)--(0,2,0),red,Arrow3);
+
+draw(Label("$x$",1),(0,0,0)--(2,0,0),red,Arrow3);
+draw(Label("$z$",1),(0,0,0)--(0,0,2.5),red,Arrow3);
+label("$z=\frac{1}{2}y^3-3x^2y$",(0.25,0.25,2),NE);
diff --git a/macros/latex/contrib/media9/doc/files/epix.prc b/macros/latex/contrib/media9/doc/files/epix.prc
new file mode 100644
index 0000000000..99e33eed23
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/epix.prc
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/epixposter.pdf b/macros/latex/contrib/media9/doc/files/epixposter.pdf
new file mode 100644
index 0000000000..92506d1092
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/epixposter.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/malte.js b/macros/latex/contrib/media9/doc/files/malte.js
new file mode 100644
index 0000000000..4d019e43a6
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/malte.js
@@ -0,0 +1,42 @@
+crank=this.scene.nodes.getByName("manivelle-1-1");
+cross=this.scene.nodes.getByName("croix-1-1");
+
+function cntrClockWise(){dir=1; speed=lastspeed;}
+function pause(){if(speed)lastspeed=speed; speed=0;}
+function clockWise(){dir=-1; speed=lastspeed;}
+function scaleSpeed(s){lastspeed*=s; if(speed) speed=lastspeed;}
+function origSpeed(){lastspeed=1; if(speed) speed=lastspeed;}
+
+Ocrank=new Vector3(-0.025, 0, 0);
+Ocross=new Vector3(0.025, 0, 0);
+
+axeZ=new Vector3(0,0,1);
+mx4x4=new Matrix4x4();
+
+var omega0=Math.PI; // init. angular frequency (half turn per second)
+var dir=1; // init. direction
+var speed=0; // speed multiplier
+var lastspeed=1;
+var alpha=0;
+
+timeEvHnd=new TimeEventHandler();
+timeEvHnd.onEvent=function(event) {
+ var dalpha=dir*speed*omega0*event.deltaTime;
+ if (dalpha!=0){
+ mx4x4.setIdentity();
+ mx4x4.rotateAboutLineInPlace(alpha,Ocrank,axeZ);
+ crank.transform.set(mx4x4);
+ with (Math){
+ if (alpha<3*PI/2) beta=0;
+ else beta=-atan(sin(alpha+PI/4)/(sqrt(2)-cos(alpha+PI/4)))+PI/4;
+ }
+ mx4x4.setIdentity();
+ mx4x4.rotateAboutLineInPlace(beta,Ocross,axeZ);
+ cross.transform.set(mx4x4);
+ alpha+=dalpha+2*Math.PI;
+ alpha%=2*Math.PI;
+ scene.update();
+ }
+}
+
+runtime.addEventHandler(timeEvHnd);
diff --git a/macros/latex/contrib/media9/doc/files/malte.u3d b/macros/latex/contrib/media9/doc/files/malte.u3d
new file mode 100644
index 0000000000..d4ea72aaa3
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/malte.u3d
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/files/random.mp4 b/macros/latex/contrib/media9/doc/files/random.mp4
new file mode 100644
index 0000000000..36be0388e9
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/files/random.mp4
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/media9.pdf b/macros/latex/contrib/media9/doc/media9.pdf
new file mode 100644
index 0000000000..fccdfd98a5
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/media9.pdf
Binary files differ
diff --git a/macros/latex/contrib/media9/doc/media9.tex b/macros/latex/contrib/media9/doc/media9.tex
new file mode 100644
index 0000000000..628384f736
--- /dev/null
+++ b/macros/latex/contrib/media9/doc/media9.tex
@@ -0,0 +1,1284 @@
+% Copyright 2012--\today Alexander Grahn
+%
+% This material is subject to the LaTeX Project Public License. See
+% http://mirrors.ctan.org/macros/latex/base/lppl.txt
+% for the details of that license.
+%
+%\pdfcompresslevel=0
+%\pdfobjcompresslevel=0
+\listfiles
+\documentclass[a4paper]{article}
+\frenchspacing
+\usepackage[utf8]{inputenc}
+\usepackage{textcomp}
+\usepackage[attachfiles]{media9}
+%\usepackage[draft]{media9}
+\usepackage{animate}
+\usepackage{fancyvrb}
+\usepackage{float}
+\usepackage{amssymb}
+\usepackage[UKenglish]{babel}
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage[T1]{fontenc}
+%\usepackage{lmodern}
+%\usepackage[scaled=0.85]{luximono}
+ \usepackage[tt=false]{libertine} %override beramono (doesn't look like tt font)
+ \usepackage[libertine]{newtxmath}
+% \usepackage[scaled=0.83]{luximono} %override beramono (doesn't look like tt font)
+\usepackage{microtype}
+%\usepackage{float}
+\usepackage{parskip}
+\usepackage{tabls}
+\usepackage{menukeys}
+\usepackage{hyperref}
+\usepackage{hypcap}
+\def\keywords{embed flash movie LaTeX pdf 3d include sound swf mp3 video mp4 h.264 aac slideshow image gallery flv audio multimedia streamed media rtmp YouTube animation JavaScript pdfLaTeX dvips ps2pdf dvipdfmx XeLaTeX u3d prc Adobe Reader Foxit RichMedia annotation LuaLaTeX}
+\hypersetup{
+ bookmarksnumbered,linktocpage,breaklinks,
+ pdftitle={The media9 Package},
+ pdfsubject={Documentation},
+ pdfauthor={Alexander Grahn},
+ pdfkeywords={\keywords},
+ allcolors=blue
+}
+\usepackage[ocgcolorlinks]{ocgx2}
+
+%\usepackage{listings}
+\usepackage{minted}
+
+\def\XeLaTeX{X\kern-.1667em\lower.5ex\hbox{\reflectbox{E}}\kern-.125em\LaTeX}
+\def\pXepLaTeX{(X\kern-.1667em\lower.5ex\hbox{\reflectbox{E}})\kern-.125em\LaTeX}
+
+\graphicspath{{files/}}
+\addmediapath{files}
+
+\hyphenation{Ja-va-Script}
+
+\newsavebox{\keyshift}
+\newsavebox{\spacebar}
+\newsavebox{\COO}
+\savebox{\COO}{$\overrightarrow{COO}$}
+\newsavebox{\CtoC}
+\savebox{\CtoC}{$\overrightarrow{C2C}$}
+
+\renewcommand{\textfraction}{0.0}
+\renewcommand{\topfraction}{1.0}
+\renewcommand{\bottomfraction}{1.0}
+
+\ifpdf\else
+ \makeatletter
+ \@latex@error{Please use pdfLaTeX to compile this documentation}
+ \makeatother
+\fi
+
+\def\parsedate#1/#2/#3\relax{
+ \def\year{#1}
+ \def\month{#2}
+ \def\day{#3}
+}
+\usepackage{siunitx}
+
+\begin{document}
+\makeatletter
+%\title{The {\sffamily media9} Package\\Version \g@mix@version@tl}
+\title{The {\sffamily media9} Package, v\g@mix@version@tl}
+\expandafter\parsedate\g@mix@date@tl\relax %set current date to package date
+\makeatother
+\author{Alexander Grahn\\[1ex]\hypersetup{breaklinks=false}\url{https://gitlab.com/agrahn/media9}\hypersetup{breaklinks=true}}
+\maketitle
+
+\begin{abstract}\noindent
+A \LaTeX{} package for embedding interactive Adobe Flash (SWF) and 3D files (Adobe U3D \& PRC) as well as video and sound files or streams (FLV, MP4/H.246, MP3) into PDF documents with Adobe Reader-9/X compatibility.\\[0.2\baselineskip]
+\begin{sloppypar}\foreignlanguage{nohyphenation}{\noindent\emph{Keywords}: \keywords}\end{sloppypar}
+\end{abstract}
+
+\tableofcontents
+
+\section{Introduction}
+This package provides an interface to embed, in the first place, interactive Flash (SWF) and 3D objects (Adobe U3D \& PRC) into PDF documents. Video and audio files or streams in the popular MP4, FLV and MP3 formats can be embedded as well. However, a media player Flash component is required for playback, as will be explained shortly. Playback of multimedia files uses Adobe Flash Player, which was bundled with Adobe Reader 9 and 10 versions. Unfortunately, beginning with Adobe Reader 11, it must be installed as a separate plug-in.
+
+Among the supported media types, video and sound files require an additional Flash (SWF) application for playback, which must be either embedded into the PDF or loaded at runtime from the internet. There are numerous such players, both open-source and commercial, available on the internet. One of them is the highly configurable open-source `StrobeMediaPlayback.swf'~\cite{strobe}, maintained by Adobe and hosted on \href{http://sourceforge.net/projects/osmf.adobe/files/}{SourceForge.net}. Package `media9' comes with an enhanced version of `StrobeMediaPlayback.swf'. In addition, two simple players for video and audio, `VPlayer.swf' and `APlayer.swf' are included, which can be used instead. They provide sufficient functionality for playing embedded files and streamed media.
+
+There is yet another player bundled with `media9'. It is a simple image gallery viewer called `SlideShow.swf' which can display collections of embedded and remote images in the PNG, JPEG and GIF file formats. Remote images are downloaded at viewing time and can be configured to be refreshed at definite time intervals.
+
+Flash Player supports the efficient H.264 codec for video compression. MP4/H.264 video files can be encoded from existing video files and from numbered bitmap sequences using the \verb+ffmpeg+ (\url{http://ffmpeg.org}) or \verb+avconv+ (\url{http://libav.org}) command line tools (Libav is a fork from the FFmpeg code). In order to allow for precise seeking within video files it is necessary to encode them with a sufficient number of key frames. The command line for recoding an existing video file \verb+video.avi+ into \verb+video.mp4+ reads (one line; \verb+ffmpeg+ can be substituted with \verb+avconv+)
+\begin{center}
+\begin{Verbatim}
+ ffmpeg -i video.avi -vf scale="trunc(iw/2)*2:trunc(ih/2)*2"
+ -c:v libx264 -profile:v high -pix_fmt yuv420p
+ -g 30 -r 30 video.mp4
+\end{Verbatim}
+\end{center}
+From a sequence \verb+frame-0.png+, \verb+frame-1.png+, ... of bitmap files, an MP4 video is produced by
+\begin{Verbatim}
+ ffmpeg -i frame-%d.png -vf scale="trunc(iw/2)*2:trunc(ih/2)*2"
+ -c:v libx264 -profile:v high -pix_fmt yuv420p
+ -g 30 -r 30 video.mp4
+\end{Verbatim}
+Both examples insert a key frame (option `\verb+-g+') at every second since the frame rate is set to 30 fps. The video encoder requires even pixel numbers in both dimensions which is ensured by adding `\verb+-vf scale="..."+' to the option list.
+
+\emph{Note:} `media9' package replaces the now obsolete `movie15' package. `media9' is based on the RichMedia Annotation (Annotations are the interactive elements in a document, in PDF specification parlance.), an Adobe addition to the PDF specification~\cite{supplement}, while `movie15' uses the old multimedia framework (`Screen Annotation') of pre-9 Readers which depends on third-party plug-ins and which does not support recent media file formats.
+
+Package `media9' supports the usual PDF making workflows, i.\,e. pdf\LaTeX{}, Lua\LaTeX, \LaTeX{} $\rightarrow$ \verb+dvips+ $\rightarrow$ \verb+ps2pdf+/Distiller and \pXepLaTeX{} $\rightarrow$ \verb+(x)dvipdfmx+.
+
+The final PDF can be viewed in current Adobe Readers on MS Windows and other platforms. On Unix platforms including Linux, however, support of Flash, video and sound was discontinued at Reader version 9.4.2, probably for security reasons. PDF documents which target \href{ftp://ftp.adobe.com/pub/adobe/reader/unix/9.x/9.4.1/}{Adobe Reader 9.4.1 for Linux} should use `VPlayer9.swf' and `APlayer9.swf' (also included in the `media9' package). These media player components are compatible with the older Flash Player 9 plugin that is bundled with the Reader for Linux. Recent versions of Foxit PDF Reader, which is available on the Windows platform, are known to render embedded Flash, video and audio content. Foxit also uses Adobe Flash Player plugin. On tablets and phones running Android or iOS, ezPDF Reader was reported to play video and sound files embedded with `media9'.
+
+\section{Requirements}
+\begin{trivlist}
+\item l3kernel (\LaTeX{} package), version $\ge \ExplFileDate$
+\item pdf\TeX{}, version $\ge1.40$
+\item Ghostscript, version $\ge9.15$, or Adobe Distiller for PS to PDF conversion
+\item dvipdfmx, version $\ge 20190503$ for DVI to PDF conversion
+\item Adobe Reader, version $\ge9$, but not greater than 9.4.1 on Linux; Foxit Reader (Flash, video, audio)
+\item Adobe Flash Player plugin for Firefox
+\end{trivlist}
+
+\section{Installation}
+MiK\TeX{} and \TeX{}Live users should run the package manager for installation and updates.
+
+Otherwise, a manual installation into the \emph{local} TeX-Directory-Structure (TDS) root directory is done along the following steps:
+\begin{enumerate}
+ \item Download the TDS compliant package file `\href{http://mirror.ctan.org/install/macros/latex/contrib/media9.tds.zip}{media9.tds.zip}' from CTAN.
+ \item Find the local TDS root directory by running\\
+ {\tt\phantom{xx}kpsewhich -var-value TEXMFLOCAL}\\
+ on the command line. The local TDS root directory is intended for packages that are not maintained by the \TeX{}Live package manager.
+ \item Unzip `\verb+media9.tds.zip+' into the local TDS root directory previously found. Depending on the location of this directory, you may need to be logged in as Root/Administrator.
+ \item After installation, update the filename database by running `{\tt texhash}' on the command line. Again, Root/Administrator privileges may be required.
+\end{enumerate}
+For updating the package, repeat the steps given above.
+
+\section{Using the package}\label{pkguse}
+Invoke the package by putting the line
+\begin{verbatim}
+ \usepackage[<package options>]{media9}
+\end{verbatim}
+to the preamble of your document, i.\,e. somewhere between \verb+\documentclass+ and \verb+\begin{document}+.
+
+`media9' honours the package options:
+\begin{verbatim}
+dvipdfmx
+xetex
+bigfiles
+draft
+final
+playbutton=...
+noplaybutton
+activate=...
+deactivate=...
+windowed=...
+transparent
+passcontext
+attachfiles
+3Dplaytype=...
+3Dplaycount=...
+3Dplayspeed=...
+3Dtoolbar
+3Dnavpane
+3Dpartsattrs=...
+3Dmenu
+3Dbg=...
+3Dlights=...
+3Drender=...
+\end{verbatim}
+Except for `\verb+dvipdfmx+', `\verb+xetex+' and `\verb+bigfiles+', the options above are also available (among others) as command options and will be explained shortly. However, if used as package options they have global scope, taking effect on all embedded media in the document. In turn, command options locally override global settings. Options without an argument are boolean options and can be negated by appending `\verb+=false+'.
+
+\XeLaTeX{} will be auto-detected. Therefore package option `\verb+xetex+' is optional. However, in the case of \verb+dvipdfmx+, package option `\verb+dvipdfmx+' is mandatory because it cannot be auto-detected.
+
+\begin{animateinline}[autoplay,loop,nomouse]{1}\strut\emph{Important:}\newframe[3]\end{animateinline} If PDF is generated via DVI and Postscript by the command sequence \verb+latex+ $\rightarrow$ \verb+dvips+ $\rightarrow$ \verb+ps2pdf+, \verb+dvips+ option `\verb+-Ppdf+' should \emph{not} be set when converting the intermediate DVI into Postscript. If you cannot do without, put `\verb+-D 1200+' \emph{after} `\verb+-Ppdf+' on the command line. Users of \LaTeX-aware text editors with menu-driven toolchain invocation, such as \TeX{}nicCenter, should check the configuration of the \verb+dvips+ call.
+
+ Option `\verb+bigfiles+' is only relevant for the \verb+latex+ $\rightarrow$ \verb+dvips+ $\rightarrow$ \verb+ps2pdf+ workflow. It may be needed if large media files cause \verb+latex+ to abort with error `\Verb+TeX capacity+ \Verb+exceeded+'. See Sect.\ \ref{bugs}.\enlargethispage{2\baselineskip}
+
+
+\section{The user interface}
+Package `media9' provides commands for media inclusion (\verb+\includemedia+) and insertion of media control buttons (\verb+\mediabutton+). The latter is introduced in Sect. \ref{mbtn}.
+
+\subsection{Media inclusion}
+\begin{verbatim}
+\includemedia[<options>]{<poster text>}{
+ <main Flash (SWF) file or URL | 3D (PRC, U3D) file>}
+\end{verbatim}
+
+The last argument, \verb+<main Flash (SWF) file or URL | 3D (PRC, U3D) file>+, is the main interactive application to be inserted into the PDF. In the case of Flash, this can be a local SWF file, or a URL, such as a YouTube video player. A local file will become part of the final PDF file, while Flash content from a URL requires an internet connection when the user activates it in Adobe Reader. A URL must be fully qualified, i.\,e., starting with either `\verb+http[s]://+' or `\verb+ftp://+'. As for 3D content, Adobe Reader only supports U3D or PRC files embedded in the PDF; they cannot be loaded or streamed during runtime. The most frequent use of \verb+\includemedia+ will likely be embedding video or sound files for playback in Adobe Reader. For this we need some media player, which is an SWF file we embed as our main application. It will be configured to load, upon activation, a particular video or sound file that was embedded as a resource into the PDF or is to be streamed from the internet. This will be shown later. Note that a local file (main application or resource) will only once be physically embedded in order to keep the final PDF file size small. If the same file (identified by MD5 checksum) appears in other \verb+\includemedia+ commands, only a reference will be inserted that points to the same storage location in the PDF.
+
+Argument \verb+<poster text>+ defines the size of the rectangular region of the document page in which the media will be displayed. Moreover, \verb+<poster text>+ will be shown in case the media has not been activated.
+%(Internally, \verb+<poster text>+ is put into a \LaTeX{} box, which is used as the appearance of the PDF annotation, i.\,e. the interactive region created by \verb+\includemedia+, in PDF specification parlance.)
+\verb+<poster text>+ can be anything that \LaTeX{} can typeset, such as an \verb+\includegraphics+ command serving as a poster image, a PGF/TikZ/PSTricks inline graphics or just ordinary text. Alternatively, \verb+<poster text>+ can be left blank in which case the size of the media rectangle should be set with options `\verb+width+' and `\verb+height+'. If a non-zero size \verb+<poster text>+ was provided, it can be resized using any combination of options `\verb+width+', `\verb+height+' or `\verb+totalheight+', `\verb+keepaspectratio+' and `\verb+scale+'.
+
+A list of directories where \TeX{} searches for media and resource files can be set-up by means of
+\begin{verbatim}
+\addmediapath{<directory>}
+\end{verbatim}
+This command appends one directory at a time to the search list. To specify more directories, just use it repeatedly. The path separator is always `\verb+/+', independent from the operating system.
+
+The following section explains all command options provided. They are passed to the media inclusion command as a comma separated list enclosed in a pair of square brackets.
+
+\subsection{Command options}
+A subset of the command options (see Sect. \ref{pkguse}) can also be used as package options, which lets them apply to all embedded media. Some of the options listed here are meaningful only for a specific media type (either Flash or 3D), which will be noted explicitly if not obvious. Dedicated sections covering Flash, video and sound as well as 3D inclusion will follow later on in this document.
+
+\begin{verbatim}
+label=<label text>
+\end{verbatim}
+The media annotation is given a label, \verb+<label text>+, which should be unique. Labelled media annotations can be targeted by the media actions of a control button (see description of the \verb+\mediabutton+ command in Sect. ~\ref{mbtn}). Moreover, a reference to the RichMedia Annotation object (of type `\verb+AnnotRichMedia+') is assigned to the JavaScript variable \verb+annotRM['<label text>']+ in order to facilitate its access in JavaScript. Note that the JavaScript reference is known only after the first opening of the page containing the media.
+\begin{verbatim}
+width=<h-size>,
+height=<v-size> | totalheight=<v-size>,
+keepaspectratio
+\end{verbatim}
+Resize the media playback area. \verb+<poster text>+, if provided, is squeezed or stretched accordingly. If only one of `\verb+width+' or `\verb+[total]height+' is given, the other dimension is scaled to maintain the aspect ratio of \verb+<poster text>+. If both `\verb+width+' and `\verb+[total]height+' are given together with `\verb+keepaspectratio+', \verb+<poster text>+ is resized to fit within \verb+<h-size>+ and \verb+<v-size>+ while maintaining its original aspect ratio. Any valid \TeX{} dimension is acceptable for \verb+<h-size>+ and \verb+<v-size>+. In addition, the length commands \verb+\width+, \verb+\height+, \verb+\depth+ and \verb+\totalheight+ can be used to refer to the original dimensions of \verb+<poster text>+.
+\begin{verbatim}
+scale=<factor>
+\end{verbatim}
+Scales the playback area by \verb+<factor>+.
+\begin{verbatim}
+addresource=<local file>,
+addresource=<another local file>,
+...
+\end{verbatim}
+Every invocation of this option embeds another local file that is required to run the main Flash application or 3D file (last argument of \verb+\includemedia+). Typically, this option is used to embed video files, media player skins, XML files (such as databases), additional objects to appear in a 3D scene etc. If an already embedded file is needed in another \verb+\includemedia+ command, this option must be given there again. However, the file in question will only once be physically embedded in order to keep the PDF file small.
+\begin{verbatim}
+flashvars={<some_var=some_val&another_var=another_val&...>}
+\end{verbatim}
+(Flash only) Usually, Flash applications can be configured via ActionScript (AS) variables the programmer of the application has made visible from outside. A typical use would be to set the video source of a media player to point to an embedded MP4 file or to a live stream, or to set the speaker volume for playback of an MP3 file. The argument of the \verb+flashvars+ option is a list of \verb+<AS variable>=<value>+ pairs separated by `\verb+&+' and enclosed in a pair of braces (\verb+{...}+).
+
+Note: If a variable is to be set to point to an embedded resource, the value of the variable must be given in exactly the same way as with the `\verb+addresource+' option. Otherwise the name of the embedded file cannot be resolved. For example,
+\begin{verbatim}
+ addresource=path/to/video.mp4
+\end{verbatim}
+implies
+\begin{verbatim}
+ flashvars={vid=path/to/video.mp4&...}
+\end{verbatim}
+if, for a particular media player, the video source is set through ActionScript variable `\verb+vid+'.
+
+(Note for 3D) Resource files used in 3D scenes cannot be loaded by means of ActionScript variables. This must be done by 3D JavaScript during activation of the 3D scene in the Reader. 3D JavaScript can be attached using option `\verb+add3Djscript+', see below.
+\begin{verbatim}
+attachfiles
+\end{verbatim}
+If set, embedded files can be downloaded from the PDF via the \Acrobatmenu{ShowHideFileAttachment}{Attachments} navigation pane in the Reader.
+\begin{verbatim}
+activate=onclick | pageopen | pagevisible
+\end{verbatim}
+Decides on how to activate the media annotation. `\verb+activate=onclick+' is default behaviour and does not need be given explicitly; embedded media is activated when the user clicks on it or by a JavaScript. It is recommended to provide a poster image with the \verb+<poster text>+ argument in that case. `\verb+pageopen+' and `\verb+pagevisible+' automatically activate the media when the page becomes visible; `{\tt pagevisible}' is better for two-up and continuous page display.
+\begin{verbatim}
+deactivate=onclick | pageclose | pageinvisible
+\end{verbatim}
+Decides on how to de-activate the media annotation. `\verb+deactivate=pageclose+' is default behaviour and does not need be given explicitly; media is automatically de-activated when the user leaves the page containing the media. `\verb+pageinvisible+' is similar, but may be better for two-up and continuous page display. Setting `\verb+deactivate+ \verb+=onclick+' requires user interaction for de-activating the media, either by right-click and chosing `\emph{\sffamily Disable Content}' or by a JavaScript.
+\hypertarget{draftfinal}{}%
+\begin{verbatim}
+draft
+final
+\end{verbatim}
+With `\verb+draft+' the media is not embedded. Instead, a box is inserted that has the dimensions of \verb+<poster text>+, subject to the resizing options `\verb+width+', `\verb+height+', `\verb+totalheight+' and `\verb+scale+'. Option `\verb+final+' does the opposite as it forces the media to be embedded. Both options can be used to reduce compilation time during authoring of a document. To get the most out of them it is recommended to set `\verb+draft+' globally as a package or class option and to set `\verb+final+' locally as a command option of the media annotation that is currently worked on. After the document has been finished, the global `\verb+draft+' option can be removed.
+\begin{verbatim}
+playbutton[= fancy | plain | none]
+noplaybutton
+\end{verbatim}
+By default, a transparent play button is laid over the inactive media annotation to draw the reader's attention to the embedded multimedia content. It is provided in two versions, `\verb+fancy+' and `\verb+plain+', but only `\verb+plain+' is available in the \XeLaTeX{} workflow. The default setting is to try the `\verb+fancy+' version. `\verb+noplaybutton+' or `\verb+playbutton=none+' disable the play button overlay.
+\begin{verbatim}
+windowed[= false | [<width>x<height>][@<position>] ]
+\end{verbatim}
+The media is played in a floating window, instead of being played in an embedded fashion. The floating window size is specified via the optional argument \verb+<width>x<height>+, where \verb+<width>+ and \verb+<height>+ are given in pixels (integer numbers without unit). If the size is not given, a default size is guessed from the annotation size. Optionally, the position of the floating window on the screen can be specified through \verb+@<position>+, where \verb+<position>+ may assume one of `\verb+tl+', `\verb+cl+', `\verb+bl+', `\verb+bc+', `\verb+br+', `\verb+cr+', `\verb+tr+', `\verb+tc+' or `\verb+cc+'. The position specifiers have the following meaning:\quad{\fboxsep=1pt\fbox{\vtop{\hbox{\ttfamily\fbox{\rule{0pt}{1.5ex}tl} \fbox{\rule{0pt}{1.5ex}tc} \fbox{\rule{0pt}{1.5ex}tr}} \hbox{\ttfamily\fbox{\rule{0pt}{1.5ex}cl} \fbox{\rule{0pt}{1.5ex}cc} \fbox{\rule{0pt}{1.5ex}cr}} \hbox{\ttfamily\fbox{\rule{0pt}{1.5ex}bl} \fbox{\rule{0pt}{1.5ex}bc} \fbox{\rule{0pt}{1.5ex}br}}}}}\\[0.5ex]
+Default window position is `\verb+cc+', that is, centred on the screen. `\verb+false+' can be set to override a global setting via package options.
+\begin{verbatim}
+transparent
+\end{verbatim}
+Indicates whether underlying page content is visible through transparent areas of the embedded media. Default is `\verb+transparent=false+'; media artwork is drawn over an opaque background prior to composition over the page content.
+\begin{verbatim}
+passcontext
+\end{verbatim}
+(Flash only) If set, user right-clicks are passed through to the context menu of the embedded Flash application, replacing the default Adobe Reader context menu. Useful for cases where the Flash programmer provided additional functionality through the context menu of his application.
+\begin{verbatim}
+3Dtoolbar
+\end{verbatim}
+Indicates whether a 3D toolbar should be shown in the Reader on top of the embedded 3D model.
+\begin{verbatim}
+3Dnavpane
+\end{verbatim}
+If set, the 3D navigation pane displaying the \Acrobatmenu{ShowHideModelTree}{3D Model Tree} becomes visible in the Reader when the content is initially activated.
+\begin{verbatim}
+3Dcoo=<x> <y> <z>
+\end{verbatim}
+\verb+<x> <y> <z>+ specify the positional vector $\overrightarrow{COO}$ of the centre of orbit of the virtual camera. Real numbers in fixed and floating point notation are accepted.
+\begin{verbatim}
+3Dc2c=<x> <y> <z>
+\end{verbatim}
+\verb+<x> <y> <z>+ specify a direction vector $\overrightarrow{C2C}$ of arbitrary length, originating in the centre of orbit and pointing to the virtual camera. Real numbers in fixed and floating point notation are accepted.
+\begin{verbatim}
+3Droll=<roll>
+\end{verbatim}
+Prescribes an initial camera roll around the optical axis (in clockwise direction, if \verb+<roll>+ is greater that zero); measured in degrees and given as fixed or floating point real number.
+\begin{verbatim}
+3Dc2w=<12 element camera-to-world matrix>
+\end{verbatim}
+This option directly sets the camera-to-world transformation matrix according to the PDF specification. This is an expert option to be used \emph{instead} of the `\verb+3Dc2c+', `\verb+3Dcoo+' and `\verb+3Droll+' options. Only fixed point real numbers are accepted.
+\begin{verbatim}
+3Dpsob=Min | Max | W | H
+\end{verbatim}
+Expert option which directly sets either the \verb+/PS+ entry in the case of perspective projection or the \verb+/OB+ entry in the case of orthographic projection to one of the four possible values. Default value is \verb+Min+.
+\begin{verbatim}
+3Droo=<r>
+\end{verbatim}
+\verb+<r>+ is a positive fixed or floating point number specifying the radius of orbit $ROO$ of the virtual camera. Good values can be found by means of the `\verb+3Dmenu+' option.
+\begin{verbatim}
+3Daac=<angle>
+\end{verbatim}
+This option sets the aperture angle of the camera, measured in degrees, for the perspective view mode. Fixed and floating point real numbers between 0 and 180 are admissible. A sensible value of 30 is pre-set by default. Larger values can be used to achieve wide-angle or fish-eye effects. See example~\ref{u3dex} in section~\ref{sec:3Dintro}. This option excludes the use of the `\verb+3Dortho+' option.
+\begin{verbatim}
+3Dortho[=<orthographic scaling factor>]
+\end{verbatim}
+Switches from the default perspective to orthographic view mode. In orthographic view, the 3D object is parallelly projected onto the virtual camera chip. The projected image is scaled by \verb+<orthographic scaling factor>+ before reaching the camera chip; default value is 1. The optimal value for the scaling factor is given by $1/D$, where $D$ is the diameter of the smallest enclosing sphere of the 3D object in World coordinate units. Fixed and floating point real numbers are accepted. The camera should be positioned outside the 3D object. For this, the radius of orbit (option `\verb+3Droo+') should be greater than $D/2$. Good values for orthographic scaling and orbital radius can easily be found by means of the `\verb+3Dmenu+' option. Option `\verb+3Dortho+' excludes the use of the `\verb+3Daac+' option.
+\hypertarget{3dmenu}{}%
+\begin{verbatim}
+3Dmenu
+\end{verbatim}
+Mainly used during document authoring. Adds three entries, `\emph{\sffamily Generate Default View}', `\emph{\sffamily Get Current View}' and `\emph{\sffamily Cross Section}' to the context (right-click) menu of an activated 3D annotation. Moreover, it allows single parts or part groups of the scene to be scaled, translated and rotated against the remaining scene objects using the keyboard. Their new position can be saved in the current view (`\emph{\sffamily Get Current View}'). At first, parts to be modified must be highlighted by clicking either into the scene or into the \Acrobatmenu{ShowHideModelTree}{3D Model Tree} (the part's bounding box becomes visible). Then, arrow keys \keys{\arrowkeyleft}, \keys{\arrowkeyright} let the part spin around the vertical axis, and \keys{\arrowkeyup}, \keys{\arrowkeydown} tilt against it. In order to spin parts around their local up-axis, keep \keys{\ctrl} pressed while using \keys{\arrowkeyleft} and \keys{\arrowkeyright}. Keys \keys{X}, \keys{\shift+X}, \keys{Y}, \keys{\shift+Y}, \keys{Z}, \keys{\shift+Z} translate the selected part along the World axes, and \keys{S}, \keys{\shift+S} scale the part.
+
+`\emph{\sffamily Generate Default View}' computes optimal camera settings such that the visible parts of the 3D scene fit tightly into the viewing area. The result is printed, formatted as a list of \verb+\includemedia+ options, into the JavaScript console. The calculation is based on the 3D object size and its position in the World coordinate system as well as the current viewing mode (perspective or orthographic).
+
+`\emph{\sffamily Cross Section}' is a toggle switch to add or remove a cross section to or from the current view. If a part of the 3D scene was previously selected, the central rotating point of the cutting plane is put into the part's centre, otherwise into the target point of the camera. The cutting plane can be rotated around the vertical axis and tilted against its upright position using the arrow keys \keys{\arrowkeyleft}, \keys{\arrowkeyright}, \keys{\arrowkeyup} and \keys{\arrowkeydown}. Keys \keys{X}, \keys{\shift+X}, \keys{Y}, \keys{\shift+Y}, \keys{Z}, \keys{\shift+Z} move the cutting plane along the World axes, and \keys{S}, \keys{\shift+S} scale its size.
+
+`\emph{\sffamily Get Current View}' writes camera settings, any part alterations, an optional cross section as well as part and scene rendering attributes of the current view into the JavaScript console. The output is a readily formatted \verb+VIEW+ section to be inserted into or appended to a file of predefined views. See option `\verb+3Dviews+'. All settings reachable via the `\emph{\sffamily Part Options}' and `\emph{\sffamily Viewing Options}' context menu items are written to the \verb+VIEW+ section.
+\begin{verbatim}
+3Dbg=<r> <g> <b>
+\end{verbatim}
+This option sets the background colour of the 3D scene. Only fixed point real numbers in the range from 0 to 1 are allowed for the colour components. Option `\verb+transparent+' may not be set at the same time.
+\begin{verbatim}
+3Dlights=<lighting scheme>
+\end{verbatim}
+Sets the default lighting scheme. The following values are honoured: `\verb+None+', `\verb+White+', `\verb+Day+', `\verb+Night+', `\verb+Hard+', `\verb+Primary+', `\verb+Blue+', `\verb+Red+', `\verb+Cube+', `\verb+CAD+', `\verb+HeadLamp+'. The default is to use the lighting scheme as specified within the 3D artwork.
+\begin{verbatim}
+3Drender=Solid | Transparent[:<opacity>] | ...
+\end{verbatim}
+\begin{flushleft}
+Sets the default render mode of the scene. The following values are honoured: `\verb+Solid+', `\verb+SolidWireframe+', `\verb+Transparent+', `\verb+TransparentWireframe+', `\verb+BoundingBox+', `\verb+TransparentBoundingBox+', `\verb+TransparentBoundingBoxOutline+', `\verb+Wireframe+', `\verb+ShadedWireframe+', `\verb+HiddenWireframe+', `\verb+Vertices+', `\verb+ShadedVertices+', `\verb+SolidOutline+', `\verb+Illustration+', `\verb+ShadedIllustration+'. Options `\verb+Transparent+' and `\verb+TransparentWireframe+' take an optional value between 0 and 1 that controls the opacity of the rendered 3D object.
+\end{flushleft}
+\begin{verbatim}
+3Dpartsattrs=restore | keep
+\end{verbatim}
+When the user selects another view from the list of predefined views (see option `\verb+3Dviews+'), attributes of individual parts, such as opacity, visibility, render mode,
+translation in space,
+which all can be set from within the Reader or by means of a file of predefined views, are reset to their original states as defined in the embedded 3D file, before any new part settings are applied. This default behaviour can be overridden by `\verb+3Dpartsattrs=keep+'. This will preserve current part attributes when the user selects another predefined 3D view in the Reader.
+\hypertarget{3Dviews}{}%
+\begin{verbatim}
+3Dviews=<views file>
+\end{verbatim}
+Instead of or in addition to the default view (options `\verb+3Dcoo+', `\verb+3Dc2c+', `\verb+3Droll+', `\verb+3Droo+', `\verb+3Daac+', `\verb+3Dortho+'), further \emph{named views} can be predefined in an auxiliary file \verb+<views file>+. Besides the virtual camera position, it is possible to adjust the rendering attributes, such as visibility and transparency, as well as position and scaling of every single part in the 3D scene. Moreover, background colour and scene lighting can be set individually for every view. The additional views can later be selected either from a drop down list in the tool bar that is associated with the activated 3D object in the Reader or from the context menu of the 3D object.
+
+The file \verb+<views file>+ is structured into view sections, one for every view:
+\begin{verbatim}
+ VIEW[=<optional name>]
+ COO=<x> <y> <z>
+ C2C=<x> <y> <z>
+ ROLL=<roll>
+ % C2W=<camera-to-world matrix> % instead of COO, C2C and ROLL
+ ROO=<roo>
+ AAC=<aac>
+ % ORTHO[=<othographic scaling factor>] % instead of AAC
+ BGCOLOR=<r> <g> <b>
+ RENDERMODE=<render mode>
+ LIGHTS=<lighting scheme>
+ CROSSSECT
+ CENTER=<x> <y> <z>
+ NORMAL=<x> <y> <z>
+ OPACITY=<cutting plane opacity>
+ VISIBLE=true | false
+ PLANECOLOR=<r> <g> <b>
+ INTERSECTIONVISIBLE=true | false
+ INTERSECTIONCOLOR=<r> <g> <b>
+ END
+ PARTSATTRS=keep
+ PART=<part name as in the Model Tree (required, optional if UTF16NAME present)>
+ UTF16NAME=<part name as hex encoded Unicode string>
+ VISIBLE=true | false
+ OPACITY=<part opacity>
+ RENDERMODE=<part render mode>
+ TRANSFORM=<12 element transformation matrix>
+ END
+ PART=<...>
+ ...
+ END
+ etc.
+ END
+
+ VIEW
+ ...
+ END
+
+ etc.
+\end{verbatim}
+A view section starts with the keyword \verb+VIEW+, optionally followed by a name for the view, and ends with the keyword \verb+END+. If no name is given to the view, a default one is created, consisting of `View' followed by the number of the current \verb+VIEW+ section in the file. A \verb+VIEW+ section may contain optional entries for setting the camera position and global rendering attributes of the scene, a \verb+CROSSSECT+ subsection as well as \verb+PART+ subsections for setting rendering and other attributes of parts individually. Table~\ref{tab:views} lists the entries in a \verb+VIEW+ section.
+
+Part sub-sections are opened by \verb+PART=<part name>+ and closed by \verb+END+. There may be as many part subsections as there are parts in a 3D scene. Table~\ref{tab:parts} lists the possible entries in a \verb+PART+ sub-section. All entries are optional. However, a \verb+UTF16NAME+ entry is recommended, as the part name may contain non-ASCII characters. The value of the \verb+UTF16NAME+ key is the part name as a hex-encoded Unicode string. If \verb+UTF16NAME+ is not used, the part name in the 3D file must be entirely composed of ASCII characters. In that case, \verb+<part name>+ is mandatory and must match the part name as indicated in the \Acrobatmenu{ShowHideModelTree}{3D Model Tree} of the 3D object (accessible via right-click onto the model in the Reader). The part can be scaled and repositioned by means of a \verb+TRANSFORM+ entry which takes a 12-element transformation matrix as its value. Remaining entries in a part sub-section control the visual appearance of the part.
+
+A view section may contain at most one \verb+CROSSSECT+ sub-section. It inserts a cutting plane at a definite position and orientation in the 3D space, controlled by optional \verb+CENTER+ and \verb+NORMAL+ entries. The appearance characteristics of the cutting plane and the intersection of the plane with the 3D geometry are controlled by optional \verb+OPACITY+, \verb+VISIBLE+, \verb+PLANECOLOR+, \verb+INTERSECTIONVISIBLE+, \verb+INTERSECTIONCOLOR+ entries. See Table~\ref{tab:crosss} for explanation.
+
+The views file can be commented. As usual, comments start with the percent sign.
+
+To facilitate the creation of a views file, option `\verb+3Dmenu+' can be added to \verb+\includemedia+ (see above). It creates context (right-click) menu entry `Get Current View' which outputs a complete \verb+VIEW+ section corresponding to the current view of the 3D object in the Reader, including camera position, an optional cross section, and all part and viewing options that can be modified via the 3D toolbar (option `\verb+3Dtoolbar+') or the context menu of the 3D object (entries `\emph{\sffamily Part Options}', `\emph{\sffamily Viewing Options}'). Hence, apart from tweaking one or another entry, there should be no need for writing views files by hand.
+\begin{verbatim}
+3Dplaytype=linear | oscillating
+\end{verbatim}
+According to the PDF specification, embedded keyframe animations can be played in two ways. If set to `\verb+linear+', keyframe animations are driven linearly from beginning to end, while `\verb+oscillating+' lets the animation play in a forth-and-back manner.
+\begin{verbatim}
+3Dplaycount=<integer number>
+\end{verbatim}
+A non-negative \verb+<integer number>+ represents the number of times the animation is played. A negative integer indicates that the animation is infinitely repeated. This value is ignored if option \verb+3Dplaytype+ is not set.
+\begin{verbatim}
+3Dplayspeed=<positive number>
+\end{verbatim}
+This option can be used to adjust the keyframe animation speed. A value of `1' corresponds to the default speed defined in the 3D file.
+\hypertarget{add3Djscript}{}%
+\begin{verbatim}
+add3Djscript=<3D JavaScript file>,
+add3Djscript=<another 3D JavaScript file>,
+...
+\end{verbatim}
+Things like animation, lighting, background of 3D objects etc. may also be script driven. Every invocation of `\verb+add3Djscript+' associates another JavaScript file with the 3D object. Upon activation of the 3D object, the scripts are executed once in the order of their inclusion. Refer to the Acrobat 3D JavaScript Reference~\cite{jscript3D} for syntax details. The following 3D JavaScript loads an image file that was attached by `\verb+addresource=images/sunset.jpg+' and uses it as the scene background.
+\begin{verbatim}
+ sunset = new Image(new Resource('pdf://images/sunset.jpg'));
+ reh = new RenderEventHandler();
+ reh.onEvent = function(event) {
+ runtime.removeEventHandler(this);
+ event.canvas.background.image=sunset;
+ }
+ runtime.addEventHandler(reh);
+\end{verbatim}
+\hypertarget{3dscripts}{}For convenience, subdirectory `javascript' of the `media9' installation contains three 3D JavaScript files which may come in handy at times: `animation.js' enables embedded keyframe animation in 3D files; `3Dspintool.js' enables the Spin tool of the 3D plugin for easier rotating the 3D object with the mouse; `asylabels.js' adds `billboard behaviour' to text labels in Asymptote ($\ge v2.17$) generated PRC files for improved visibility; text labels always face the camera while rotating the 3D object with the mouse.
+
+\begin{table}[bp]%[p]%[htbp]
+ \centering
+ \caption{Entries in a {\tt VIEW} section.}\label{tab:views}
+ \begin{tabular}[t]{p{0.24\linewidth}lp{0.42\linewidth}}\hline
+ key & type & remarks\\\hline\hline
+ {\tt COO} & three numbers & centre of orbit, see option `{\tt 3Dcoo}'\\
+ {\tt C2C} & three numbers & centre of orbit to camera vector, see option `{\tt 3Dc2c}'\\
+ {\tt ROO} & number & radius of orbit, see option `{\tt 3Droo}'\\
+ {\tt C2W} & 12 numbers & camera-to-world transformation matrix, see option `{\tt 3Dc2w}'\\
+ {\tt AAC} & number & camera aperture angle, see option `{\tt 3Daac}'\\
+ {\tt ORTHO} & number (optional) & enables orthographic view, see option `{\tt 3Dortho}'\\
+ {\tt PSOB} & string & expert setting, see option `{\tt 3Dpsob}'\\
+ {\tt ROLL} & number & camera roll, see option `{\tt 3Droll}'\\
+ {\tt BGCOLOR} & three numbers & given as {\tt<r> <g> <b>}, specify the 3D scene background colour (RGB), see option `{\tt 3Dbg}'\\
+ {\tt RENDERMODE} & string & render mode of the 3D object, see option `{\tt 3Drender}'\\
+ {\tt LIGHTS} & string & lighting scheme, see option `{\tt 3Dlights}'\\
+ {\tt PARTSATTRS} & string & allowed values are `{\tt keep}' and `{\tt restore}'; decides on whether to restore or not original part attributes before applying new ones from this view; see option `{\tt 3Dpartsattrs}'\\
+ {\tt PART}\,\,(sub-sec\-tion) & string & part name as in the \Acrobatmenu{ShowHideModelTree}{3D Model Tree}; name argument is optional if a {\tt UTF16NAME} entry is present in the sub-section opened by a {\tt PART} keyword, otherwise required; see Table~\ref{tab:parts} for list of possible entries\\
+ {\tt CROSSSECT}~(sub-section) & -- & see Table~\ref{tab:crosss} for list of possible entries\\
+ \hline
+ \end{tabular}
+\end{table}
+
+\begin{table}%[p]%[htbp]
+ \centering
+ \caption{Entries in a {\tt PART} sub-section.}\label{tab:parts}
+ \begin{tabular}[t]{llp{0.5\linewidth}}\hline
+ key & type & remarks\\\hline\hline
+ {\tt UTF16NAME} & hex string & part name in UTF-16 (aka Unicode), encoded as a hexadecimal string; optional, but useful for part names composed of non-latin characters;\\
+ {\tt VISIBLE} & boolean & a flag (`{\tt true}' or `{\tt false}') indicating the visibility of this part\\
+ {\tt OPACITY} & number & a number between 0.0 and 1.0 specifying the opacity of this part\\
+ {\tt RENDERMODE} & string & rendermode of this part, overrides global {\tt RENDERMODE} value in parent {\tt VIEW} section, see option `{\tt 3Drender}'\\
+ {\tt TRANSFORM} & 12 numbers & transformation matrix defining the part's position and scaling\\
+ \hline
+ \end{tabular}
+\end{table}
+
+\begin{table}%[p]%[htbp]
+ \centering
+ \caption{Entries in a {\tt CROSSSECT} sub-section.}\label{tab:crosss}
+ \begin{tabular}[t]{llp{0.5\linewidth}}\hline
+ key & type & remarks\\\hline\hline
+ {\tt CENTER} & three numbers & central point coordinates of the cutting plane\\
+ {\tt NORMAL} & three numbers & normal vector coordinates of the cutting plane pointing into the cut-off region\\
+ {\tt OPACITY} & number & a number between 0.0 and 1.0 specifying the opacity of the cutting plane\\
+ {\tt VISIBLE} & boolean & a flag (`{\tt true}' or `{\tt false}') indicating the visibility of the cutting plane\\
+ {\tt PLANECOLOR} & three numbers & given as {\tt<r> <g> <b>}, specify the colour (RGB) of the cutting plane\\
+ {\tt INTERSECTIONVISIBLE} & boolean & a flag (`{\tt true}' or `{\tt false}') indicating the visibility of the intersection of the cutting plane with any 3D geometry\\
+ {\tt INTERSECTIONCOLOR} & three numbers & given as {\tt<r> <g> <b>}, specify the colour (RGB) for the cutting plane's intersection with the 3D geometry\\
+ \hline
+ \end{tabular}
+\end{table}
+
+\clearpage
+\subsection{Control buttons}\label{mbtn}
+\begin{verbatim}
+\mediabutton[<options>]{<normal button text or graphic>}
+\end{verbatim}
+This command inserts a clickable button for media control. Actions to be performed are specified through options `\verb+mediacommand+', `\verb+3Dgotoview+' and `\verb+jsaction+'. By using these options repeatedly and in any combination, several actions can be bound to one media button, and one media button can be used to control several media at the same time. Media actions are started in the given order but performed in parallel, because they do not wait for each other to finish. The target of an action is specified via the label that was also given to a particular media by the `\verb+label+' option of `\verb+\includemedia+'. Individual button faces can be defined for the `mouse-over' and `mouse-button-down' events using the `\verb+overface+' and `\verb+downface+' options. Without options, the button produced does nothing. The options provided are as follows:
+\begin{verbatim}
+overface=<mouse-over text or graphic>
+\end{verbatim}
+If specified, the media button changes its appearance when the mouse pointer is moved over it. Without this option, the button appearance does not change. An \verb+\includegraphics+ command may need to be enclosed in braces.
+\begin{verbatim}
+downface=<mouse-button-down text or graphic>
+\end{verbatim}
+If specified, the media button changes its appearance when the mouse button is pressed while the pointer is over it. An \verb+\includegraphics+ command may need to be enclosed in braces.
+\begin{verbatim}
+tooltip=<tip text>
+\end{verbatim}
+A box with \verb+<tip text>+ is shown when the mouse pointer is moved over the button.
+\begin{verbatim}
+3Dgotoview=<label text>[:<view specification>]
+\end{verbatim}
+Selects a view from the list of predefined views associated with a 3D media inclusion (see option `\verb+3Dviews+'). The target media is specified by \verb+<label text>+, as defined by the `\verb+label+' option of `\verb+\includemedia+'. \verb+<label text>+ alone without a view specification simply activates the 3D object if not yet activated. {\tt<view specification>} which is separated from the label by a colon (\verb+:+) can be one of the following: an integer specifying the zero-based index into the list of views in the 3D views file; one of `\verb+D+', `\verb+F+', `\verb+L+', `\verb+N+', `\verb+P+' indicating the default, first, last, next or previous view in the list of views; a string delimited by `\verb+(+' and `\verb+)+' matching the name of a view as specified by the `\verb+VIEW=...+' entry in the views file. The option can be given several times to simultaneously change the view in more than one 3D inclusion. However, it cannot be used to create an animation effect within the same 3D inclusion, because \verb+3Dgotoview+ actions are executed in parallel.
+%For this, one may implement a 3D JavaScript function that is associated with the media button using the `\verb+mediacommand+' option.
+\begin{verbatim}
+mediacommand=<label text>[:<command> [(<arg1>) (<arg2>) ...]]
+\end{verbatim}
+A media command \verb+<command>+, with arguments if required, is sent to a media inclusion identified by \verb+<label text>+, as defined by the `\verb+label+' option of `\verb+\includemedia+'. \verb+<label text>+ alone without a command specification simply activates the media, if not yet activated. The option can be used multiple times within the same button to target different media inclusions at the same time or to execute several commands for the same media. Depending on the type of the target media (3D or Flash), \verb+<command>+ is either the name of a JavaScript function defined in a 3D JavaScript file associated with the 3D media (see option `\verb+add3Djscript+') or the name of an ActionScript function that was exposed by the embedded Flash file. ActionScript functions are exposed to the scripting context of the hosting document by using the \verb+ExternalInterface+ call within the Flash file. Arguments to be passed to \verb+<command>+ must be enclosed in `\verb+(+' and `\verb+)+' and separated by spaces, the whole list be finally enclosed in `\verb+[+' and `\verb+]+', even if there is only a single argument. Arguments can be of Boolean type (\verb+true+, \verb+false+), numbers (integer, reals) and strings. The number of arguments and their types must match the definition of the function to be called. Media players VPlayer.swf and APlayer.swf shipping with `media9' expose a number of ActionScript functions that can be used with this option (see Tab. \ref{AVPlayerMethods}). \verb+<command> [(<arg1>) (<arg2>) ...]+ must be enclosed in braces if there are embedded equals signs or commas. Examples of using `\verb+mediacommand+' are given in Figs.~\ref{videoa} and \ref{radio}c.
+\begin{verbatim}
+jsaction=[<label text>:]{<JavaScript code>}
+\end{verbatim}
+The JavaScript code is executed in the context of the document's instance of the JavaScript engine (there is one instance of the JavaScript engine per open document in Adobe Reader). \verb+<JavaScript code>+ is required and must be enclosed in braces. Unlike media actions defined with options `\verb+mediacommand+' and `\verb+3Dgotoview+', the JavaScript action defined here is not targeted at a particular embedded media and can be used to run arbitrary code. Therefore, \verb+<label text>+ is optional. If provided, it must be separated from \verb+<JavaScript code>+ by a colon. However, it is recommended to provide a label text. It ensures that \verb+annotRM['<label text>']+ is a valid JavaScript reference to the \verb+AnnotRichMedia+ object. \verb+annotRM['<label text>']+ can be used to get access to the global context of the annotation's instance of the 3D JavaScript engine (there is one instance of the 3D JavaScript engine per activated RichMedia Annotation with 3D content). The 3D JavaScript context of a 3D model can be accessed as \verb+annotRM['<label text>'].context3D+. Refer to the Acrobat 3D JavaScript Reference~\cite{jscript3D} for details on built-in JavaScript objects that are available in the 3D context. The \verb+annotRM['<label text>'].callAS()+ method may be used as an alternative to the `\verb+mediacommand+` option. Both serve as means of running ActionScript functions exposed by embedded Flash files. See~\cite{jscript} for details.
+\begin{verbatim}
+draft
+final
+\end{verbatim}
+See \hyperlink{draftfinal}{above}.
+
+\clearpage
+\section[Embedding Flash, video and sound, image slide-shows (with examples)]{Embedding Flash, video and sound,\\ image slide-shows}
+A YouTube video clip, as shown in Fig.~\ref{alien}, may serve as a basic example of loading Flash content from a URL to be displayed in an embedded fashion in a PDF document. Indeed, a YouTube clip is nothing more than a small SWF file which loads a video stream and other necessary resources, such as user controls and a player skin from a remote server. It can be configured via ActionScript variables to play several videos in a row, to play a video in a loop etc. Player parameters are documented on \url{http://code.google.com/apis/youtube/player_parameters.html} and can be passed to the player using either the `\verb+flashvars+' option, as in the example, or appended to the URL string after the video ID. A question mark `\verb+?+' must be put between the video ID and the parameter string. Some of the documented parameters, such as `\verb+rel+', seem to have an effect only if they are passed as part of the URL.
+
+\begin{figure}[H]
+\centering
+\begin{Verbatim}
+\includemedia[
+ width=0.6\linewidth,height=0.3375\linewidth, % 16:9
+ activate=pageopen,
+ flashvars={
+ modestbranding=1 % no YT logo in control bar
+ &autohide=1 % controlbar autohide
+ &showinfo=0 % no title and other info before start
+ &rel=0 % no related videos after end
+ }
+]{}{http://www.youtube.com/v/r382kfkqAF4?rel=0}
+\end{Verbatim}
+\vspace{1ex}
+\includemedia[
+ width=0.6\linewidth,height=0.3375\linewidth,
+ activate=pageopen,
+ flashvars={
+ modestbranding=1 % no YT logo in control bar
+ &autohide=1 % controlbar autohide
+ &showinfo=0 % no title and other info before start
+ &rel=0 % no related videos after end
+ }
+]{}{http://www.youtube.com/v/r382kfkqAF4?rel=0}
+\caption{A YouTube video as an example of a Flash application loaded from a URL.}\label{alien}
+\end{figure}
+
+Media files (video, sound, images) are always loaded and then played by a media player application. Four players are installed along with the `media9.sty' package file: three simple players for video (`VPlayer.swf'), sound (`APlayer.swf') and slide-shows of embedded and/or remote (live or static) image files (`SlideShow.swf'), and the fully blown third-party media player `StrobeMediaPlayback.swf' with some fixes to improve its usability. The simple players are `chromeless', that is, they do not have graphical user controls. Nevertheless, interactivity is provided through left mouse button press and release for playing, pausing, resuming playback, through the player's context menu (right-click), and through the keyboard as summarized in Table \ref{kbcontrol}. The player apps were compiled using the open-source Apache Flex SDK~\cite{flex} from XML source files which reside in the \verb+doc/+ folder of the package installation. For `StrobeMediaPlayback.swf', only a patch file is included, as the sources can be downloaded elsewhere.
+
+The improvements of `StrobeMediaPlayback.swf' in comparision to the original version on SourceForge.net are
+\begin{itemize}
+ \item fix: video could not be restarted after end of playback if ActionScript variable `{\tt autoRewind}' is set to `{\tt false}'
+ \item new: first frame of video is shown as default poster instead of black stage
+ \item new: play/pause video by clicking on the stage (as with `VPlayer.swf'), useful in a lecture situation
+\end{itemize}
+
+There is no need to copy the installed players into the directory of the document source for embedding. They will be found by \LaTeX{} without taking any further action.
+
+Like YouTube videos, media players are configured via ActionScript variables which are passed using option `\verb+flashvars+'. Table~\ref{playerparams} lists parameters available for `VPlayer.swf', `APlayer.swf' and `SlideShow.swf', Table~\ref{smpparams} for `StrobeMediaPlayback.swf'.
+
+Playback of embedded video files is shown in Fig.~\ref{videoa}. Besides embedded files, also video streamed from remote servers via HTTP and RTMP protocols is supported, as shown in Fig.~\ref{videob}.
+
+Sound files and streams in the MP3 format can be played with `APlayer.swf'. Fig.~\ref{radio} contains examples of an audio live stream and a remote MP3 sound file. In one of the sound examples, the player is loaded from a CTAN mirror during runtime because an internet connection is required anyway for streaming the audio. If a local sound file is to be embedded into the PDF this would have to be done in the same way as with the video file in one of the previous examples using the `\verb+addresource+' option.
+
+The slide-show application `SlideShow.swf' requires a configuration file in the XML file format. It specifies the URLs from which the images will be loaded and may contain optional settings, such as image captions, refresh intervals for live images and initial rotation. The syntax is documented in the configuration file template in Fig.~\ref{SlideShowXML}. The file name of a configuration file is passed to `SlideShow.swf' using the `\verb+xml+' ActionScript variable. Note that for security reasons the configuration file and `SlideShow.swf' must reside on the same server or be both embedded into the PDF. Otherwise, a security error will be thrown by the Flash Player plugin. A slide-show example is shown in Fig.~\ref{slideshowex}.
+
+`VPlayer.swf', `APlayer.swf' and `SlideShow.swf' expose a number of ActionScript functions to the JavaScript engine of Adobe Reader, allowing for playback control of media through push buttons (see Sect. \ref{mbtn}) and various trigger events. The functions and their calling convention are listed in Tables~\ref{AVPlayerMethods} and~\ref{SlideShowMethods}. From within JavaScript, these functions can be called using the `\verb+callAS+' (=~call ActionScript) method of the \verb+AnnotRichMedia+ object. As an example, a call to the `\verb+seek+' function looks like
+\begin{verbatim}
+ annotRM.myvideo.callAS("seek", 12.3);
+\end{verbatim}
+and a call to the parameter-less function `\verb+playPause+' like
+\begin{verbatim}
+ annotRM.myvideo.callAS("playPause");
+\end{verbatim}
+Both functions calls are sent to the media inclusion that was labelled `\verb+myvideo+' using the `\verb+label+' option of \verb+\includemedia+. An example of playing and pausing a video clip and setting the video source via interactive push buttons is given in Fig.~\ref{videoa}.
+
+\begin{table}[htbp]
+%\flushright
+\centering
+\caption{Keyboard control of media players (a) `VPlayer.swf' \& `APlayer.swf', (b) `SlideShow.swf'. Players must have the focus to take effect. Click onto the players if necessary.}\label{kbcontrol}
+\vspace{3ex}
+(a)\\
+\begin{tabular}{rp{0.5\linewidth}}\hline
+keys & action\\\hline\hline
+\keys{\unexpanded{\makebox[2\width][c]{Space}}} & play/pause\\
+\keys{Home}, \keys{End} & go to start/end\\
+\keys{\arrowkeyleft}, \keys{\arrowkeyright} & seek backwards/forwards\\
+\keys{\arrowkeydown}, \keys{\arrowkeyup} & decrease/increase speaker volume\\
+\keys{m} & mute/unmute\\
+\keys{\ctrl+\arrowkeyleft}, \keys{\ctrl+\arrowkeydown}, \keys{\ctrl+\arrowkeyright} & (APlayer.swf only) change sound speaker balance\\\hline
+\end{tabular}
+
+\vspace{6ex}
+(b)\\
+\begin{tabular}{rp{0.5\linewidth}}\hline
+keys & action\\\hline\hline
+\keys{\unexpanded{\makebox[2\width][c]{Space}}} & play/pause auto-cycling through slides\\
+\keys{\arrowkeyleft}, \keys{\arrowkeyright} & previous/next slide\\
+\keys{Home}, \keys{End} & go to first/last slide\\
+\keys{r}, \keys{R} & right/left rotate current slide by \SI{90}{\degree}\\\hline
+\end{tabular}
+\end{table}
+
+\begin{table}[tp]
+\centering
+\caption{Parameters (ActionScript variables) for media players (a) `VPlayer.swf' \& `APlayer.swf', (b) `SlideShow.swf' shipping with media9. Parameters are passed as a `{\tt\&}'-separated string using `{\tt flashvars}' option.}\label{playerparams}
+\vspace{3ex}
+(a)\\
+\begin{tabular}[c]{p{0.4\linewidth}p{0.5\linewidth}}\hline
+parameter & description\\\hline\hline
+{\tt source=<file path or URL>} & (required) path to embedded media file (see option `{\tt addresource}'), or URL (http, rtmp) to online media file\\
+{\tt autoPlay=true|false} & if {\tt=true}, automatically starts playback after activation (see option `{\tt activation})'\\
+{\tt autoRewind=true|false} &(VPlayer.swf only) if {\tt=true}, automatically rewind to the first frame after playback has finished; default is `{\tt false}'\\
+{\tt loop=true|false} & if {\tt=true}, media is played in a loop\\
+{\tt scaleMode=letterbox|none| \phantom{.}\hfill{}stretch|zoom} & default: {\tt letterbox}; determines how to scale the video in order to fit into player\\
+{\tt hideBar=true|false} &(APlayer.swf only) if {\tt=true}, the progress bar indicating the play position is not shown\\
+{\tt volume=<value between 0.0 \phantom{.}\hfill{}and 1.0>} & sets volume of the sound\\
+{\tt balance=<value between \phantom{xxxx} \phantom{.}\hfill{}-1.0 and 1.0>} & (APlayer.swf only) sets balance of sound speakers\\\hline
+\end{tabular}
+
+\vspace{6ex}
+(b)\\
+\begin{tabular}[c]{p{0.4\linewidth}p{0.5\linewidth}}\hline
+parameter & description\\\hline\hline
+{\tt xml=<file path or URL>} & (required) path to embedded (option `{\tt addresource}') or URL of remote configuration file\\
+{\tt delay=<number>} & sets time delay in seconds for transition between slides during playback, default: \SI{5}{\second}\\
+{\tt autoPlay=true|false} & if {\tt=true}, automatically starts cycling through slides after activation (see option `{\tt activation})'\\\hline
+\end{tabular}
+\end{table}
+
+\begin{table}%[bp]
+\centering
+\caption{Exposed ActionScript functions of media players `VPlayer.swf' and `APlayer.swf' that can be called from within media buttons (see Sect. \ref{mbtn}) or from JavaScript using the `{\tt callAS}' method of the `{\tt AnnotRichMedia}' JavaScript object (see~\cite{jscript} for further information).}\label{AVPlayerMethods}
+\begin{tabular}[t]{lp{0.25\linewidth}p{0.5\linewidth}}\hline
+function & argument &description\\\hline\hline
+{\tt play} & number (optional) & play media, optionally starting at \emph{number} seconds offset into the media file\\
+{\tt pause} & number (optional) & pause media, optionally at the given offset of \emph{number} seconds into the media file\\
+{\tt playPause} & & toggle between play and pause\\
+{\tt setSource} & string & load another media file (path to file, embedded using option `{\tt addresource}', or URL)\\
+{\tt seek} & number & move the play location to a time offset from the beginning of the media; argument measured in seconds\\
+{\tt rewind} & & rewind media to the beginning (without pausing it)\\
+{\tt volume} & number between 0 and 1 & set volume level\\
+{\tt balance} & number between $-1$ and $+1$ & (APlayer.swf only) set speaker balance\\
+{\tt mute} & & mute or unmute (toggle) the audio of the media\\
+{\tt currentTime}& & returns current playhead position in seconds; only useful in JavaScript via `{\tt callAS}' method\\
+{\tt duration} & & returns duration of the media video/sound file currently loaded, only useful in JavaScript via `{\tt callAS}' method\\
+{\tt playing} & & returns boolean value `{\tt true}', if the media is currently playing, `{\tt true}' otherwise; only useful in JavaScript via `{\tt callAS}' method\\
+{\tt muted} & & returns boolean value `{\tt true}', if the sound is currently muted, `{\tt false}' otherwise; only useful in JavaScript via `{\tt callAS}' method\\\hline
+\end{tabular}
+\end{table}
+
+\begin{table}%[bp]
+\centering
+\caption{Exposed ActionScript functions of slide-show player `SlideShow.swf' that can be called from within media buttons or from JavaScript.}\label{SlideShowMethods}
+\begin{tabular}[t]{lp{0.21\linewidth}p{0.47\linewidth}}\hline
+function & argument &description\\\hline\hline
+{\tt play} & & play slide-show\\
+{\tt pause} & & pause slide-show\\
+{\tt playPause} & & toggle between play and pause\\
+{\tt setXML} & string & load another configuration file (path to file, embedded using option `{\tt addresource}', or URL)\\
+{\tt seek} & int number & go to slide `number' (zero-based)\\
+{\tt slideNum} & & returns current slide number (zero-based); only useful in JavaScript via `{\tt callAS}' method\\
+{\tt numSlides} & & returns total number of slides in the slide-show; only useful in JavaScript via `{\tt callAS}' method\\
+{\tt playing} & & returns boolean value `{\tt true}', if the slide-show is currently playing, `{\tt true}' otherwise; only useful in JavaScript via `{\tt callAS}' method\\
+{\tt rotate} & int number (optional) & rotate current slide by number$\times\SI{90}{\degree}$\\\hline
+\end{tabular}
+\end{table}
+
+\begin{table}[bp]
+\centering
+\caption{Parameters (ActionScript variables) for `StrobeMediaPlayback.swf' shipping with media9. Parameters are passed as a `{\tt\&}'-separated string using `{\tt flashvars}' option.}\label{smpparams}
+\begin{tabular}[t]{p{0.4\linewidth}p{0.5\linewidth}}\hline
+parameter & description\\\hline\hline
+{\tt src=<file path or URL>} & (required) path to embedded media file (see option `{\tt addresource}'), or URL (http, rtmp) of online media file\\
+{\tt autoPlay=true|false} & default: {\tt false}; if {\tt=true}, automatically starts playback after activation (see option `{\tt activation})'\\
+{\tt autoRewind=true|false} & default: {\tt true}; if {\tt=false}, keep last frame after end of playback\\
+{\tt loop=true|false} & if {\tt=true}, media is played in a loop\\
+{\tt scaleMode=letterbox|none| \phantom{.}\hfill{}stretch|zoom} & default: {\tt letterbox}; determines how to scale the video in order to fit into player\\
+{\tt controlBarMode=docked|\phantom{xxxxxxxxxxxxxx} \phantom{.}\hfill{}floating|none} & default: {\tt docked}; determines position and visibility of control bar\\
+{\tt controlBarAutoHide=\phantom{xxxxxxx} \phantom{.}\hfill{}true|false} & default: {\tt true}; automatically hide or not control bar\\
+{\tt controlBarAutoHideTimeout= \phantom{.}\hfill{}<number [s]>} & default: {\tt 3}; time span before auto-hide\\
+{\tt volume=<value between 0.0 \phantom{.}\hfill{}and 1.0>} & sets volume of the sound\\
+{\tt audioPan=<value between \phantom{xxxx} \phantom{.}\hfill{}-1.0 and 1.0>} & default: {\tt 0}; sets balance of sound speakers\\
+{\tt muted=true|false} & default: {\tt false}; mute or not sound\\
+%{\tt clipStartTime=<number [s]>} & default: {\tt NaN}; time to start from\\
+%{\tt clipEndTime=<number [s]>} & default: {\tt NaN}; time to end at\\
+\hline
+\end{tabular}
+\end{table}
+
+\begin{figure}[p]
+\centering
+\begin{Verbatim}
+\includemedia[
+ label=some_dice,
+ width=0.6\linewidth,height=0.45\linewidth,
+ addresource=random.mp4, %two video files
+ addresource=cube.mp4,
+ transparent, %transparent player background
+ activate=pageopen,
+ passcontext, %show VPlayer's right-click menu
+ flashvars={
+ source=random.mp4
+ &loop=true % loop video
+ }
+]{}{VPlayer.swf}
+
+\mediabutton[
+ mediacommand=some_dice:playPause,
+ overface=\color{blue}{\fbox{\strut Play/Pause}},
+ downface=\color{red}{\fbox{\strut Play/Pause}}
+]{\fbox{\strut Play/Pause}}
+\mediabutton[
+ mediacommand=some_dice:setSource [(random.mp4)]
+]{\fbox{\strut random.mp4}}
+\mediabutton[
+ mediacommand=some_dice:setSource [(cube.mp4)]
+]{\fbox{\strut cube.mp4}}
+\end{Verbatim}
+\vspace{1ex}
+\makebox[\linewidth]{\hfill\includemedia[
+ label=some_dice,
+ width=0.6\linewidth,height=0.45\linewidth,
+ addresource=random.mp4, %two video files
+ addresource=cube.mp4,
+ transparent, %transparent player background
+ activate=pageopen,
+ passcontext, %show VPlayer's right-click menu
+ flashvars={
+ source=random.mp4
+ &loop=true % loop video
+ }
+]{}{VPlayer.swf}\hfill}
+\makebox[\linewidth]{\hfill%
+\mediabutton[
+ mediacommand=some_dice:playPause,
+ overface=\color{blue}{\fbox{\strut Play/Pause}},
+ downface=\color{red}{\fbox{\strut Play/Pause}}
+]{\fbox{\strut Play/Pause}}
+\mediabutton[
+ mediacommand=some_dice:setSource [(random.mp4)]
+]{\fbox{\strut random.mp4}}
+\mediabutton[
+ mediacommand=some_dice:setSource [(cube.mp4)]
+]{\fbox{\strut cube.mp4}}
+\hfill}
+\caption{Example of playing back two different embedded MP4 video files in the same video player instance. The player, `VPlayer.swf', is also embedded in the PDF. Exposed ActionScript functions `{\tt playPause}' and `{\tt setSource}' of `VPlayer.swf' (Table~\ref{AVPlayerMethods}) are used to set-up media control buttons. Different button faces have been defined for the Play/Pause button. Also, playback can be contolled via the player's context (right-click) menu or the keyboard.}\label{videoa}
+\end{figure}
+
+\begin{figure}%[bp]
+\thispagestyle{empty}
+\centering
+%\lstinputlisting[basicstyle=\scriptsize\ttfamily,language=XML]{config.xml}
+\vspace{-10\baselineskip}
+\hskip -5em\inputminted[fontsize=\footnotesize]{xml}{files/config.xml}
+\caption{Configuration file template (XML format) for `SlideShow.swf'}\label{SlideShowXML}
+\end{figure}
+
+\begin{figure}[bp]
+\makeatletter%
+\begingroup% locally fixing \Acrobatmenu for use in \caption
+\def\HyColor@UseColor#1{\color{#1}}%
+\def\Acrobatmenu#1#2{\leavevmode\pdfstartlink attr{\Hy@setpdfborder\ifx\@pdfhighlight\@empty\else/H\@pdfhighlight\fi\ifx\@menubordercolor\relax\else/C[\@menubordercolor]\fi}user{/Subtype/Link\ifHy@pdfa/F 4\fi/A<</S/Named/N/#1>>}\relax\Hy@colorlink\@menucolor#2\close@pdflink}%
+\makeatother%
+\savebox{\keyshift}{\keys{\shift+}}%
+\savebox{\spacebar}{\keys{\unexpanded{\makebox[2\width][c]{Space}}}}%
+\centering
+\begin{Verbatim}
+\includemedia[
+ width=\linewidth,height=\linewidth,
+ activate=pageopen,
+ addresource=files/config.xml, %embedded configuration
+ addresource=files/cubeposter.png, %embedded image file
+ flashvars={xml=files/config.xml},
+ passcontext %show the player's context menu
+]{}{SlideShow.swf}
+\end{Verbatim}
+\vspace{1ex}
+\makebox[\linewidth]{\hfill%
+\includemedia[
+% width=0.8\linewidth,height=0.6\linewidth,
+ width=\linewidth,height=\linewidth,
+ activate=pageopen,
+ addresource=files/config.xml, %embedded configuration
+ addresource=files/cubeposter.png, %embedded image file
+ flashvars={xml=files/config.xml},
+ passcontext %show the player's context menu
+]{}{SlideShow.swf}
+\hfill}
+\caption{Slide-show example with one embedded and several remote image files, one of which is a live image. Images 2 and 3 are examples of remote images with variable resolution; the best fitting image resolution is requested from the server depending on the current display size. The listing in Fig.~\ref{SlideShowXML} is used as content of file \Acrobatmenu{ShowHideFileAttachment}{`config.xml'}. Manually cycle forwards through the images by mouse-click or backwards by \usebox{\keyshift}+mouse-click. Play/pause auto-cycling through the slide-show by hitting \usebox{\spacebar}. Also, the context (right-click) menu of the player can be used.}\label{slideshowex}
+\endgroup
+\end{figure}
+
+\begin{figure}[bp]
+\centering
+\begin{Verbatim}
+\includemedia[
+ width=0.6\linewidth,height=0.3375\linewidth, % 16:9
+ activate=pageopen,
+ flashvars={
+ src=rtmp://streaming.music.indiana.edu:1935/onDemand/mp4:media/%
+ 20090327_VarRussianTheme-h264-480.m4v
+ &scaleMode=stretch
+ }
+]{}{StrobeMediaPlayback.swf}
+\end{Verbatim}
+\vspace{1ex}
+\makebox[\linewidth]{\hfill\includemedia[
+ width=0.6\linewidth,height=0.3375\linewidth,
+ activate=pageopen,
+ flashvars={
+ src=rtmp://streaming.music.indiana.edu:1935/onDemand/mp4:media/20090327_VarRussianTheme-h264-480.m4v
+ &scaleMode=stretch
+ }
+]{}{StrobeMediaPlayback.swf}\hfill}
+\caption{Example of video streamed from an RTMP server. This example uses media player `StrobeMediaPlayBack.swf', physically embedded in the PDF.}\label{videob}
+\end{figure}
+
+\begin{figure}[bp]
+%\flushright
+{\small%
+\begin{Verbatim}
+ \includemedia[
+ addresource=bird.mp3,
+ flashvars={
+ source=bird.mp3
+ &autoPlay=true
+ },
+ transparent,
+ passcontext %show APlayer's right-click menu
+ ]{\color{blue}\framebox[0.4\linewidth][c]{Singing bird}}{APlayer.swf}
+\end{Verbatim}
+}
+\makebox[\linewidth]{(a)\hfill\includemedia[
+ addresource=bird.mp3,
+ flashvars={
+ source=bird.mp3
+ &autoPlay=true
+ },
+ transparent,
+ passcontext %show APlayer's right-click menu
+]{\color{blue}\framebox[0.5\linewidth][c]{Singing bird}}{APlayer.swf}
+\hfill}
+\vskip 4ex{\small\begin{Verbatim}
+ \includemedia[
+ flashvars={
+ source=http://mp3.live.tv-radio.com/franceculture%
+ /all/franceculturehautdebit.mp3
+ &autoPlay=true
+ },
+ transparent,
+ passcontext %show APlayer's right-click menu
+ ]{\color{blue}\fbox{Listen live to Radio France Culture}}{%
+ http://mirrors.ibiblio.org/pub/mirrors/CTAN/macros/latex/%
+ contrib/media9/players/APlayer.swf%
+ }
+\end{Verbatim}
+}
+\makebox[\linewidth]{(b)\hfill\includemedia[
+ flashvars={
+ source=http://mp3.live.tv-radio.com/franceculture/all/franceculturehautdebit.mp3
+ &autoPlay=true
+ },
+ transparent,
+ passcontext %show APlayer's right-click menu
+]{\color{blue}\fbox{Listen live to Radio France Culture}}{%
+%APlayer.swf%
+%http://mirror.ctan.org/macros/latex/contrib/media9/players/APlayer.swf%
+http://mirrors.ibiblio.org/pub/mirrors/CTAN/macros/latex/contrib/media9/players/APlayer.swf%
+}\hfill}
+\vskip 4ex
+{\small%
+\begin{Verbatim}
+ \includemedia[
+ label=song49,
+ flashvars={source=http://www.openbsd.org/songs/song49.mp3},
+ transparent,
+ passcontext %show VPlayer's right-click menu
+ ]{\color{blue}\fbox{Listen to OpenBSD 4.9 release song}}{APlayer.swf}\\
+ \mediabutton[
+ mediacommand=song49:play[(5.5)],
+ mediacommand=song49:pause[(37)]
+ ]{\fbox{First verse}}
+ \mediabutton[
+ mediacommand=song49:play[(39)],
+ mediacommand=song49:pause[(49)]
+ ]{\fbox{The Answer}}
+ \mediabutton[
+ mediacommand=song49:play[(206.5)],
+ mediacommand=song49:pause[(221)]
+ ]{\fbox{Harmonica solo}}
+\end{Verbatim}
+}
+\makebox[\linewidth]{\rule{0pt}{1.5\baselineskip}\hfill\includemedia[
+ label=song49,
+ flashvars={
+ source=http://www.openbsd.org/songs/song49.mp3
+ },
+ transparent,
+ passcontext %show VPlayer's right-click menu
+]{\color{blue}\fbox{Listen to OpenBSD 4.9 release song}}{APlayer.swf}\hfill}\\
+\makebox[\linewidth]{\makebox[0pt][l]{(c)}\hfill%
+ \mediabutton[
+ mediacommand=song49:play[(5.5)],
+ mediacommand=song49:pause[(37)]
+ ]{\fbox{First verse}}
+ \mediabutton[
+ mediacommand=song49:play[(39)],
+ mediacommand=song49:pause[(49)]
+ ]{\fbox{The Answer}}
+ \mediabutton[
+ mediacommand=song49:play[(206.5)],
+ mediacommand=song49:pause[(221)]
+ ]{\fbox{Harmonica solo}}%
+\hfill}
+\caption{Example of (a) embedded sound file, (b) streamed audio and (c) progressively downloaded MP3. ID3 tags `title', `artist' and `album' are displayed if contained in the MP3 stream or file. In (b), the sound player, APlayer.swf, is loaded from a CTAN mirror upon activation.}\label{radio}
+\end{figure}
+
+\begin{figure}[bp]
+\centering
+\begin{Verbatim}
+\includemedia[
+ %activate=onclick, % default
+ addresource=cube.mp4,
+ flashvars={
+ source=cube.mp4
+ &autoPlay=true % start playing on activation
+ &loop=true
+ },
+ passcontext %show player's right-click menu
+]{\includegraphics[height=0.45\linewidth]{cubeposter}}{VPlayer9.swf}
+\end{Verbatim}
+\vspace{1ex}
+\hfill\includemedia[
+ activate=onclick, %default
+ addresource=cube.mp4,
+ flashvars={
+ source=cube.mp4
+ &autoPlay=true %start playing on activation
+ &loop=true
+ },
+ passcontext %show player's right-click menu
+]{\includegraphics[height=0.45\linewidth]{cubeposter}}{VPlayer9.swf}\hfill
+\vspace{2ex}
+\begin{Verbatim}
+ \includemedia[
+ addresource=bird.mp3,
+ flashvars={
+ source=bird.mp3
+ &autoPlay=true
+ },
+ transparent,
+ passcontext %show player's right-click menu
+ ]{\color{blue}\framebox[0.4\linewidth][c]{Singing bird}}{APlayer9.swf}
+\end{Verbatim}
+\vspace{1ex}
+\hfill\includemedia[
+ addresource=bird.mp3,
+ flashvars={
+ source=bird.mp3
+ &autoPlay=true
+ },
+ transparent,
+ passcontext %show player's right-click menu
+]{\color{blue}\framebox[0.5\linewidth][c]{Singing bird}}{APlayer9.swf}\hfill
+\vspace{2ex}
+\caption{Video and sound examples that should run in Adobe Reader for Linux up to version \href{ftp://ftp.adobe.com/pub/adobe/reader/unix/9.x/9.4.1/}{9.4.1}. Here, players `VPlayer9.swf' and `APlayer9.swf' are used. Both are compatible with Adobe Flash Player 9 plugin that is bundled with the Reader. Also, the video player needs to be activated by mouse click (which is the default). We provide a poster image that is shown in the inactive state.}\label{ar941ex}
+\end{figure}
+
+\clearpage
+\section[Embedding 3D objects (with examples)]{Embedding 3D objects}
+\subsection{Introduction}\label{sec:3Dintro}
+Adobe Acrobat/Reader 7 was the first version to allow for embedding 3-di\-men\-sion\-al graphic objects, such as CAD models or 3D scientific data, that can be manipulated interactively by the user. U3D was the first supported format and was mainly developed by Right Hemisphere and Adobe. U3D had some deficiencies and was later replaced by the PRC format after Adobe purchased the original developer, the French company `Trade and Technology France'. U3D is still supported, but PRC is preferred as it allows for exact representation of curved surfaces and better compression. Both, U3D and PRC specifications are public~\cite{u3d,prc}.
+
+Currently, two open-source software packages are known to export into the PRC file format. The first one is Asymptote~\cite{asy}, which is a descriptive 2D and 3D vector graphics language and interpreter and which uses \TeX{} to typeset labels and equations. It allows for high quality mathematical figures and technical drawings. An impressive gallery of examples can be found on its Web site. The second one is MathGL~\cite{mathgl}, a library for scientific data visualization. It provides interfaces to a number of programming and scripting languages as well as an interpreter for its own command language `MGL'.
+
+MeshLab~\cite{meshlab} is an open-source conversion and processing software for 3D mesh data which can import from and export to a number of file formats. Its U3D export filter is based on the open-source `Universal 3D Sample Software'~\cite{u3dlib}.
+
+There are a few options to \verb+\includemedia+ which define how the 3D object is positioned within the view port of a virtual camera, or conversely, how the virtual camera is positioned and oriented within a coordinate system, called `The World', which bears the 3D object at a fixed position. Fig.~\ref{3dscene} should help to grasp the scenery: The virtual camera is orbiting at a distance of $ROO$ (option `\verb+3Droo+') around the centre of orbit, specified by the position vector $\overrightarrow{COO}$ (option `\verb+3Dcoo+'); $\sphericalangle AAC$ (option `\verb+3Daac+') is the camera's aperture angle. The direction vector $\overrightarrow{C2C}$ (option `\verb+3Dc2c+') is needed to specify the initial camera position. The camera may be given an initial roll angle (option `\verb+3Droll+') around its optical axis $(-1)\cdot\overrightarrow{C2C}$.
+\begin{figure}[ht]
+ \resizebox{\linewidth}{!}{\input{files/3dsystem}}
+ \caption{Camera and 3D object in the World System $XYZ$; centre of orbit position vector \usebox{\COO}, centre of orbit to camera direction vector \usebox{\CtoC}, radius of orbit $ROO$, aperture angle of camera $\sphericalangle AAC$.}\label{3dscene}
+\end{figure}
+Fig.~\ref{3dscene} shows the camera parameters for the perspective view mode. Alternatively, the orthographic view mode may be chosen. In orthographic view, the 3D object is parallelly projected onto the virtual camera chip. Before reaching the camera chip, the projected image must be scaled in order to fit onto the chip. Orthographic view can be enabled using the `\verb+3Dortho+' option which takes the scaling factor as its argument.
+
+Above options define the default view, i.\,e. the view that is shown initially after activating the 3D object in the Reader. Of course, once activated, the camera position can be changed using the mouse and one can change forth and back between perspective and orthographic viewing modes using the 3D tool bar.
+
+By default, the virtual camera sits at the origin $(0,0,0)$ of the World, looking in the positive $Y$ direction, i.\,e. default settings of \verb+3Droo=0+, \verb+3Dcoo=0 0 0+ and \verb+3Dc2c=0 -1 0+ are assumed. (Note that $\overrightarrow{C2C}$ is the opposite of the optical axis vector.) Thus, in order to get a `front view' of the 3D object it is sufficient to set the radius of orbit, i.\,e. the distance between camera and object appropriately. Sometimes you may want to adjust the orbital centre, i.\,e. the target of the camera as well, in particular, if the object is irregularly shaped or if it is not centred around the World origin. Fortunately, it is possible to let the values of the corresponding options be determined automatically. Choosing option `\verb+3Dmenu+' adds `\emph{\sffamily Generate Default View}' to the context menu of the activated 3D scene. Selecting this entry calculates and outputs optimal camera settings which can be inserted into the option list of \verb+\includemedia+.
+
+Additional resource files that are needed to render the 3D scene can be embedded using the `\verb+addresource+' option. Typical resources are bitmaps and Flash files (even animated and interactive ones), to be used as materials or scene backgrounds, as well as additional 3D objects in the U3D or PRC file format. The allowed file formats of \emph{bitmapped} image files depend on the \LaTeX workflow. \LaTeX{} $\rightarrow$ \verb+dvips+ $\rightarrow$ \verb+ps2pdf+/Distiller accepts PS and EPS files; pdf\LaTeX{} accepts PNG, JPEG and JBIG2; \pXepLaTeX{} $\rightarrow$ \verb+(x)dvipdfmx+ accepts PNG and JPEG. 3D JavaScript is necessary to load these resources upon activation. 3D JavaScript files are attached using the `\verb+add3Djscript+' option.
+
+Below, several examples of embedded 3D files are shown. The first one, Fig.~\ref{asyex} is a PRC file generated with Asymptote. Note the text labels always facing the camera thanks to the attached 3D JavaScript file `asylabels.js'. The second example, Fig.~\ref{u3dex}, demonstrates the use of a views file which defines additional named views of the 3D object. Moreover, the possibilities of the extended 3D context menu can be evaluated. They were enabled by adding the `\verb+3Dmenu+' option to \verb+\includemedia+. All part and scene rendering attributes that can be changed via the `\emph{\sffamily Part Options}' and `\emph{\sffamily Viewing Options}' menu entries, as well as a cross section to be added with the `\emph{\sffamily Cross Section}' menu entry can be saved into a new view (`\emph{\sffamily Get Current View}'). Position, orientation and scaling of individual parts and of the cross section can be changed using the keyboard (keys \keys{\arrowkeyleft}, \keys{\arrowkeyright}, \keys{\arrowkeyup}, \keys{\arrowkeydown}, \keys{X}, \keys{\shift+X}, \keys{Y}, \keys{\shift+Y}, \keys{Z}, \keys{\shift+Z}, \keys{S}, \keys{\shift+S}). The third example, Fig.~\ref{u3dex2}, shows an animated 3D object. The animation itself and the functions called by pressing the controls are defined in a 3D JavaScript file attached to the model.
+
+\begin{figure}[bp]
+\centering
+\begin{Verbatim}
+\includemedia[
+ width=0.8\linewidth,height=0.8\linewidth,
+ add3Djscript=asylabels.js, %upright text labels
+ add3Djscript=3Dspintool.js, %let scene rotate about z-axis
+ % 3Dcoo, 3Droo values found with `Generate Default View' from
+ % context menu
+ 3Dmenu,
+ 3Dc2c=4 2 3,
+ 3Dcoo=4.413303375244141 2.195653200149536 -0.000011444091796875,
+ 3Droo=429.49035778293376,
+]{\includegraphics{epixposter}}{epix.prc}
+\end{Verbatim}
+\vspace{1ex}
+\includemedia[
+ width=0.8\linewidth,height=0.8\linewidth,
+ add3Djscript=asylabels.js, %upright text labels
+ add3Djscript=3Dspintool.js, %let scene rotate about z-axis
+ % 3Dcoo, 3Droo values found with `Generate Default View' from
+ % context menu
+ 3Dmenu,
+ 3Dc2c=4 2 3,
+ 3Dcoo=4.413303375244141 2.195653200149536 -0.000011444091796875,
+ 3Droo=429.49035778293376,
+]{\includegraphics{epixposter}}{epix.prc}
+\caption{Embedded PRC file produced with Asymptote, making use of convenience 3D JavaScripts `asylabels.js' and `3Dspintool.js' mentioned
+{\leavevmode\pdfstartlink attr{/Border[0 0 1]/C[0 0 1]/H/I}user{/Subtype/Link/A<</D(3dscripts)/S/GoTo>>}above\pdfendlink}.}\label{asyex}
+\end{figure}
+
+\begin{figure}[bp]
+\begin{Verbatim}[fontsize=\footnotesize]
+\includemedia[
+ label=dice,
+ width=0.5\linewidth,height=0.5\linewidth,
+ activate=pageopen,
+ 3Dtoolbar, 3Dmenu,
+ 3Dviews=dice.vws,
+]{}{dice.u3d}
+
+\mediabutton[3Dgotoview=dice:N]{\fbox{Next view}}
+\mediabutton[3Dgotoview=dice:(Back)]{\fbox{View `Back'}}
+\mediabutton[3Dgotoview=dice:5]{\fbox{6th view in the list}}
+\end{Verbatim}
+\vspace{1ex}
+Contents of `dice.vws':
+\begin{Verbatim}[fontsize=\footnotesize]
+ VIEW=Front
+ ROO=27
+ END
+ VIEW=Back
+ ROO=27
+ C2C=0 1 0
+ END
+ VIEW=Left
+ ROO=27
+ C2C=-1 0 0
+ END
+ VIEW=Right
+ ROO=27
+ C2C=1 0 0
+ END
+ VIEW=Top
+ ROO=27
+ C2C=0 0 1
+ END
+ VIEW=Bottom
+ ROO=27
+ C2C=0 0 -1
+ END
+ VIEW=Fish Eye at Centre
+ AAC=130
+ END
+\end{Verbatim}
+\centering
+\raisebox{-0.7\height}{\includemedia[
+ label=dice,
+ width=0.5\linewidth,height=0.5\linewidth,
+ activate=pageopen,
+ 3Dtoolbar,
+ 3Dviews=dice.vws,
+ 3Dmenu,
+]{}{dice.u3d}}\quad\parbox[t]{0.4\linewidth}{%
+\mediabutton[3Dgotoview=dice:N]{\fbox{Next view}}\\
+\mediabutton[3Dgotoview=dice:(Back)]{\fbox{View `Back'}}\\
+\mediabutton[3Dgotoview=dice:5]{\fbox{6th view in the list}}}
+\caption{Embedded U3D file, based on a VRML model by Peter Whitehouse, \url{http://www.wonko.info/vrml/index.htm}; conversion to U3D was done using DeepExploration\textsuperscript{\scriptsize\textregistered}\cite{dexp}. The file `{\tt dice.vws}' provides predefined views. Buttons are created with {\tt\string\mediabutton} using the `{\tt 3Dgotoview}' option.}\label{u3dex}
+\end{figure}
+
+\begin{figure}
+ \begin{Verbatim}
+ \includemedia[
+ label=malte,
+ width=0.5\linewidth,height=0.5\linewidth,
+ activate=pageopen,
+ 3Dmenu,
+ 3Dc2c=1 1 1,
+ 3Dcoo=-0.001042630523443222 1.4577869224116568e-19 0.028235001489520073,
+ 3Droo=0.2604540212188131,
+ add3Djscript=malte.js
+ ]{}{malte.u3d}
+
+ \mediabutton[
+ jsaction=malte:{annotRM['malte'].context3D.cntrClockWise();}
+ ]{\includegraphics[height=1.44em]{boutona}}
+ \mediabutton[
+ jsaction=malte:{annotRM['malte'].context3D.pause();}
+ ]{\includegraphics[height=1.44em]{boutonb}}
+ \mediabutton[
+ jsaction=malte:{annotRM['malte'].context3D.clockWise();}
+ ]{\includegraphics[height=1.44em]{boutonc}}
+ \hspace{1em}
+ \mediabutton[
+ jsaction=malte:{annotRM['malte'].context3D.scaleSpeed(1/1.1);}
+ ]{\includegraphics[height=1.44em]{boutond}}
+ \mediabutton[
+ jsaction=malte:{annotRM['malte'].context3D.origSpeed();}
+ ]{\includegraphics[height=1.44em]{boutone}}
+ \mediabutton[
+ jsaction=malte:{annotRM['malte'].context3D.scaleSpeed(1.1);}
+ ]{\includegraphics[height=1.44em]{boutonf}}
+ \end{Verbatim}
+ \centering
+ \includemedia[
+ label=malte,
+ width=0.5\linewidth,height=0.5\linewidth,
+ activate=pageopen,
+ 3Dmenu,
+ 3Dc2c=1 1 1,
+ 3Dcoo=-0.001042630523443222 1.4577869224116568e-19 0.028235001489520073,
+ 3Droo=0.2604540212188131,
+ add3Djscript=malte.js
+ ]{}{malte.u3d}
+
+ \mediabutton[jsaction=malte:{annotRM['malte'].context3D.cntrClockWise();}]{\includegraphics[height=1.44em]{boutona}}
+ \mediabutton[jsaction=malte:{annotRM['malte'].context3D.pause();}]{\includegraphics[height=1.44em]{boutonb}}
+ \mediabutton[jsaction=malte:{annotRM['malte'].context3D.clockWise();}]{\includegraphics[height=1.44em]{boutonc}}
+ \hspace{1em}
+ \mediabutton[jsaction=malte:{annotRM['malte'].context3D.scaleSpeed(1/1.1);}]{\includegraphics[height=1.44em]{boutond}}
+ \mediabutton[jsaction=malte:{annotRM['malte'].context3D.origSpeed();}]{\includegraphics[height=1.44em]{boutone}}
+ \mediabutton[jsaction=malte:{annotRM['malte'].context3D.scaleSpeed(1.1);}]{\includegraphics[height=1.44em]{boutonf}}
+% \mediabutton[
+% mediacommand=malte:cntrClockWise
+% ]{\includegraphics[height=1.44em]{boutona}}
+% \mediabutton[
+% mediacommand=malte:pause
+% ]{\includegraphics[height=1.44em]{boutonb}}
+% \mediabutton[
+% mediacommand=malte:clockWise
+% ]{\includegraphics[height=1.44em]{boutonc}}
+% \hspace{1em}
+% \mediabutton[
+% mediacommand=malte:scaleSpeed [0.91]
+% ]{\includegraphics[height=1.44em]{boutond}}
+% \mediabutton[
+% mediacommand=malte:origSpeed
+% ]{\includegraphics[height=1.44em]{boutone}}
+% \mediabutton[
+% mediacommand=malte:scaleSpeed [1.1]
+% ]{\includegraphics[height=1.44em]{boutonf}}
+ \caption{Animated U3D example of a Maltese drive contributed by Jean-Luc Chesnot. The animation and the functions called in the JavaScript actions of the media buttons are defined in the JavaScript file `{\tt malte.js}'.}\label{u3dex2}
+\end{figure}
+\clearpage
+\subsection{3D quick-start guide}\label{sec:3Dtut}
+\begin{enumerate}
+ \item Insert the 3D object with default camera settings and with extended context menu enabled (option `\verb+3Dmenu+'):
+
+\begin{Verbatim}
+ \includemedia[
+ width=0.5\linewidth,height=0.5\linewidth,
+ activate=pageopen,
+ 3Dmenu
+ ]{}{myfile.u3d}
+\end{Verbatim}
+
+ \item Compile the document.
+ \item Open the PDF document in Adobe Reader and go to the page containing the 3D object. Select `\emph{\sffamily Generate Default View}' from the 3D context menu (right mouse click) and wait for the JavaScript console to pop up. Optionally, drag the object with the mouse to change the viewpoint of the camera and select `\emph{\sffamily Generate Default View}' again. This will re-adjust the distance between camera and target to fit all visible parts tightly into the viewport. The options printed into the console are updated accordingly.
+ \item Copy the camera settings ({\tt 3Droo=...}, {\tt 3Dcoo=...}, etc.) from the console into the option list of {\tt \string\include\-media}.
+ \item Compile the document again.
+ \xdef\lastcount{\theenumi}
+\end{enumerate}
+
+\emph{Optional steps} (option `{\tt 3Dmenu}' required):
+
+\begin{enumerate}
+ \setcounter{enumi}{\lastcount}
+ \item Additional, named views; cross sections; rescaled, repositioned parts:
+ \begin{itemize}
+ \item[a)] Open a text file, e.\,g. `myviews.vws', to be populated with additional views of the 3D object.
+ \item[b)] Manipulate the 3D object using the mouse (camera position) and via 3D context menu items `\emph{\sffamily Part Options}' and `\emph{\sffamily Viewing Options}' (visibility, rendering attributes, background etc.); the camera target can be moved into the centre of a single part via `\emph{\sffamily Part Options}'$\rightarrow$`\emph{\sffamily Zoom to Part}'.
+ \item[c)] Add a cross section plane (select `\emph{\sffamily Cross Section}' from the 3D context menu), adjust its position using the keyboard; keyboard keys are given \hyperlink{3dmenu}{here}.
+ \item[d)] Adjust scaling and position of individual parts using the keyboard; keyboard keys are given \hyperlink{3dmenu}{here}.
+ \item[e)] Re-adjust the camera distance using either `\emph{\sffamily Generate Default View}' or `\emph{\sffamily Part Options}'$\rightarrow$`\emph{\sffamily Fit Visible}'.
+ \item[f)] When you are done, select `\emph{\sffamily Get Current View}' to get the {\tt VIEW} section, readily formatted for insertion into the views file. Repeat steps (a)--(f) to get any number of views you want to define. The views file can be edited manually to give meaningful names to the views (change the value of the {\tt VIEW} key), or to further tweak camera settings, opacity, part options etc.
+ \item[g)] Attach the views file with option `{\tt 3Dviews}':
+\begin{Verbatim}
+ \includemedia[
+ width=0.5\linewidth,height=0.5\linewidth,
+ activate=pageopen,
+ 3Dviews=myviews.vws,
+ 3Dmenu
+ ]{}{myfile.u3d}
+\end{Verbatim}
+ If you are satisfied with the predefined views in the views file, the default view first specified through the options of {\tt\string\includemedia} can be deleted. The first view in the views file becomes the default view then.
+ \end{itemize}
+ \item Associate any number of 3D JavaScript files with the 3D object:
+\begin{Verbatim}
+ \includemedia[
+ width=0.5\linewidth,height=0.5\linewidth,
+ activate=pageopen,
+ add3Djscript=somescript.js,
+ add3Djscript=otherscript.js,
+ 3Dviews=myviews.vws,
+ 3Dmenu
+ ]{}{myfile.u3d}
+\end{Verbatim}
+A few 3D JavaScript files ready to be used are already installed along with `media9.sty', see \hyperlink{3dscripts}{above}.
+\end{enumerate}
+
+\section{Caveats}\label{bugs}
+\begin{enumerate}
+ \item Large media files may cause \TeX{} to interrupt with error
+\begin{verbatim}
+! TeX capacity exceeded, sorry [main memory size=3000000].
+\end{verbatim}
+when using \verb+latex+ in \verb+dvips+ mode. While writing the DVI file, media files in the current page that are about to be embedded are kept in \TeX's memory until shipping out of the readily typeset page. In the case of large or many files, this may be more than \TeX{} can cope with by default.
+
+There are two options to handle such situations:
+
+The first one is to increase \TeX's main memory. You may follow the steps in the \href{http://mirror.ctan.org/macros/latex/contrib/animate/animate.pdf\#dest:mem}{Bugs} section of the `animate' package documentation. In \TeX{}Live-2012, the maximum value that can be set is \verb+main_memory = 12435455+.
+
+If increasing \TeX's main memory does not help, use the package option `\verb+bigfiles+' with media9. It defers file embedding from the DVI producing to the PS producing step.
+
+ \item In right-to-left typesetting context (RTL) using the (pdf)\LaTeX{} or \XeLaTeX{} engines, the \verb+<poster text>+ argument of \verb+\includemedia+ and the text arguments for button faces of the \verb+\mediabutton+ command should be enclosed in pairs of \verb+\beginR+ and \verb+\endR+.
+\end{enumerate}
+
+\section{Acknowledgements}
+This package was written using the new \LaTeX3 syntax which was a lot of fun. Many thanks to the \LaTeX3 team!
+
+\begin{thebibliography}{9}
+ \bibitem{strobe} Adobe Systems Inc.: \emph{Strobe Media Playback}, 2010, available at \url{http://osmf.org/strobe_mediaplayback.html}
+
+ \bibitem{supplement} Adobe Systems Inc.: \emph{Adobe Supplement to ISO 32000, BaseVersion 1.7, ExtensionLevel 3}, 2008, available at \url{http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/adobe_supplement_iso32000.pdf}
+
+ \bibitem{jscript3D} Adobe Systems Inc.: \emph{JavaScript for Acrobat 3D Annotations API Ref\-er\-ence}, available at \url{http://livedocs.adobe.com/acrobat_sdk/10/Acrobat10_HTMLHelp/JS_3D_Intro.90.1.html}
+
+ \bibitem{jscript} Adobe Systems Inc.: \emph{JavaScript for Acrobat API Reference}, available at \url{http://livedocs.adobe.com/acrobat_sdk/10/Acrobat10_HTMLHelp/JS_API_AcroJSPreface.87.1.html}
+
+ \bibitem{flex} The Apache Software Foundation: \emph{Apache Flex SDK}, available at \url{http://flex.apache.org}
+
+ \bibitem{u3d} ECMA International: \emph{Universal 3D File Format (ECMA-363), 4th Edition}, 2007, available at \url{http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-363\%204th\%20Edition.pdf}
+
+ \bibitem{prc} Adobe Systems Inc.: \emph{PRC Format Specification}, available at \url{http://livedocs.adobe.com/acrobat_sdk/10/Acrobat10_HTMLHelp/API_References/PRCReference/PRC_Format_Specification/index.html}
+
+ \bibitem{asy} A. Hammerlindl, J. Bowman and T. Prince: \emph{Asymptote: The Vector Graphics Language}, available at \url{http://asymptote.sourceforge.net}
+
+ \bibitem{mathgl} A. A. Balakin: \emph{MathGL - library for scientific graphics}, available at \url{http://mathgl.sourceforge.net}
+
+ \bibitem{u3dlib} T. O'Rourke, T. Strelchun: \emph{Universal 3D Sample Software}, available at \url{http://sourceforge.net/projects/u3d}
+
+ \bibitem{meshlab} P. Cignoni \emph{et al.}: \emph{MeshLab}, available at \url{http://meshlab.sourceforge.net}
+
+ \bibitem{dexp} RightHemisphere Inc.: \emph{DeepExploration}, \url{http://www.righthemisphere.com/products/dexp/}
+% \bibitem{swftools} R. B\"ohme, M. Kramm: \emph{Swftools}, available at \url{http://www.swftools.org}
+\end{thebibliography}
+
+\end{document}
diff --git a/macros/latex/contrib/media9/javascript/3Dmenu.js b/macros/latex/contrib/media9/javascript/3Dmenu.js
new file mode 100644
index 0000000000..9037d040dd
--- /dev/null
+++ b/macros/latex/contrib/media9/javascript/3Dmenu.js
@@ -0,0 +1,854 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) 2012--today, Alexander Grahn
+//
+// 3Dmenu.js
+//
+// version 20140923
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 3D JavaScript used by media9.sty
+//
+// Extended functionality of the (right click) context menu of 3D annotations.
+//
+// 1.) Adds the following items to the 3D context menu:
+//
+// * `Generate Default View'
+//
+// Finds good default camera settings, returned as options for use with
+// the \includemedia command.
+//
+// * `Get Current View'
+//
+// Determines camera, cross section and part settings of the current view,
+// returned as `VIEW' section that can be copied into a views file of
+// additional views. The views file is inserted using the `3Dviews' option
+// of \includemedia.
+//
+// * `Cross Section'
+//
+// Toggle switch to add or remove a cross section into or from the current
+// view. The cross section can be moved in the x, y, z directions using x,
+// y, z and X, Y, Z keys on the keyboard, be tilted against and spun
+// around the upright Z axis using the Up/Down and Left/Right arrow keys
+// and caled using the s and S keys.
+//
+// 2.) Enables manipulation of position and orientation of indiviual parts and
+// groups of parts in the 3D scene. Parts which have been selected with the
+// mouse can be scaled moved around and rotated like the cross section as
+// described above. To spin the parts around their local up-axis, keep
+// Control key pressed while using the Up/Down and Left/Right arrow keys.
+//
+// This work may be distributed and/or modified under the
+// conditions of the LaTeX Project Public License.
+//
+// The latest version of this license is in
+// http://mirrors.ctan.org/macros/latex/base/lppl.txt
+//
+// This work has the LPPL maintenance status `maintained'.
+//
+// The Current Maintainer of this work is A. Grahn.
+//
+// The code borrows heavily from Bernd Gaertners `Miniball' software,
+// originally written in C++, for computing the smallest enclosing ball of a
+// set of points; see: http://www.inf.ethz.ch/personal/gaertner/miniball.html
+//
+////////////////////////////////////////////////////////////////////////////////
+//host.console.show();
+
+//constructor for doubly linked list
+function List(){
+ this.first_node=null;
+ this.last_node=new Node(undefined);
+}
+List.prototype.push_back=function(x){
+ var new_node=new Node(x);
+ if(this.first_node==null){
+ this.first_node=new_node;
+ new_node.prev=null;
+ }else{
+ new_node.prev=this.last_node.prev;
+ new_node.prev.next=new_node;
+ }
+ new_node.next=this.last_node;
+ this.last_node.prev=new_node;
+};
+List.prototype.move_to_front=function(it){
+ var node=it.get();
+ if(node.next!=null && node.prev!=null){
+ node.next.prev=node.prev;
+ node.prev.next=node.next;
+ node.prev=null;
+ node.next=this.first_node;
+ this.first_node.prev=node;
+ this.first_node=node;
+ }
+};
+List.prototype.begin=function(){
+ var i=new Iterator();
+ i.target=this.first_node;
+ return(i);
+};
+List.prototype.end=function(){
+ var i=new Iterator();
+ i.target=this.last_node;
+ return(i);
+};
+function Iterator(it){
+ if( it!=undefined ){
+ this.target=it.target;
+ }else {
+ this.target=null;
+ }
+}
+Iterator.prototype.set=function(it){this.target=it.target;};
+Iterator.prototype.get=function(){return(this.target);};
+Iterator.prototype.deref=function(){return(this.target.data);};
+Iterator.prototype.incr=function(){
+ if(this.target.next!=null) this.target=this.target.next;
+};
+//constructor for node objects that populate the linked list
+function Node(x){
+ this.prev=null;
+ this.next=null;
+ this.data=x;
+}
+function sqr(r){return(r*r);}//helper function
+
+//Miniball algorithm by B. Gaertner
+function Basis(){
+ this.m=0;
+ this.q0=new Array(3);
+ this.z=new Array(4);
+ this.f=new Array(4);
+ this.v=new Array(new Array(3), new Array(3), new Array(3), new Array(3));
+ this.a=new Array(new Array(3), new Array(3), new Array(3), new Array(3));
+ this.c=new Array(new Array(3), new Array(3), new Array(3), new Array(3));
+ this.sqr_r=new Array(4);
+ this.current_c=this.c[0];
+ this.current_sqr_r=0;
+ this.reset();
+}
+Basis.prototype.center=function(){return(this.current_c);};
+Basis.prototype.size=function(){return(this.m);};
+Basis.prototype.pop=function(){--this.m;};
+Basis.prototype.excess=function(p){
+ var e=-this.current_sqr_r;
+ for(var k=0;k<3;++k){
+ e+=sqr(p[k]-this.current_c[k]);
+ }
+ return(e);
+};
+Basis.prototype.reset=function(){
+ this.m=0;
+ for(var j=0;j<3;++j){
+ this.c[0][j]=0;
+ }
+ this.current_c=this.c[0];
+ this.current_sqr_r=-1;
+};
+Basis.prototype.push=function(p){
+ var i, j;
+ var eps=1e-32;
+ if(this.m==0){
+ for(i=0;i<3;++i){
+ this.q0[i]=p[i];
+ }
+ for(i=0;i<3;++i){
+ this.c[0][i]=this.q0[i];
+ }
+ this.sqr_r[0]=0;
+ }else {
+ for(i=0;i<3;++i){
+ this.v[this.m][i]=p[i]-this.q0[i];
+ }
+ for(i=1;i<this.m;++i){
+ this.a[this.m][i]=0;
+ for(j=0;j<3;++j){
+ this.a[this.m][i]+=this.v[i][j]*this.v[this.m][j];
+ }
+ this.a[this.m][i]*=(2/this.z[i]);
+ }
+ for(i=1;i<this.m;++i){
+ for(j=0;j<3;++j){
+ this.v[this.m][j]-=this.a[this.m][i]*this.v[i][j];
+ }
+ }
+ this.z[this.m]=0;
+ for(j=0;j<3;++j){
+ this.z[this.m]+=sqr(this.v[this.m][j]);
+ }
+ this.z[this.m]*=2;
+ if(this.z[this.m]<eps*this.current_sqr_r) return(false);
+ var e=-this.sqr_r[this.m-1];
+ for(i=0;i<3;++i){
+ e+=sqr(p[i]-this.c[this.m-1][i]);
+ }
+ this.f[this.m]=e/this.z[this.m];
+ for(i=0;i<3;++i){
+ this.c[this.m][i]=this.c[this.m-1][i]+this.f[this.m]*this.v[this.m][i];
+ }
+ this.sqr_r[this.m]=this.sqr_r[this.m-1]+e*this.f[this.m]/2;
+ }
+ this.current_c=this.c[this.m];
+ this.current_sqr_r=this.sqr_r[this.m];
+ ++this.m;
+ return(true);
+};
+function Miniball(){
+ this.L=new List();
+ this.B=new Basis();
+ this.support_end=new Iterator();
+}
+Miniball.prototype.mtf_mb=function(it){
+ var i=new Iterator(it);
+ this.support_end.set(this.L.begin());
+ if((this.B.size())==4) return;
+ for(var k=new Iterator(this.L.begin());k.get()!=i.get();){
+ var j=new Iterator(k);
+ k.incr();
+ if(this.B.excess(j.deref()) > 0){
+ if(this.B.push(j.deref())){
+ this.mtf_mb(j);
+ this.B.pop();
+ if(this.support_end.get()==j.get())
+ this.support_end.incr();
+ this.L.move_to_front(j);
+ }
+ }
+ }
+};
+Miniball.prototype.check_in=function(b){
+ this.L.push_back(b);
+};
+Miniball.prototype.build=function(){
+ this.B.reset();
+ this.support_end.set(this.L.begin());
+ this.mtf_mb(this.L.end());
+};
+Miniball.prototype.center=function(){
+ return(this.B.center());
+};
+Miniball.prototype.radius=function(){
+ return(Math.sqrt(this.B.current_sqr_r));
+};
+
+//functions called by menu items
+function calc3Dopts () {
+ //create Miniball object
+ var mb=new Miniball();
+ //auxiliary vector
+ var corner=new Vector3();
+ //iterate over all visible mesh nodes in the scene
+ for(i=0;i<scene.meshes.count;i++){
+ var mesh=scene.meshes.getByIndex(i);
+ if(!mesh.visible) continue;
+ //local to parent transformation matrix
+ var trans=mesh.transform;
+ //build local to world transformation matrix by recursively
+ //multiplying the parent's transf. matrix on the right
+ var parent=mesh.parent;
+ while(parent.transform){
+ trans=trans.multiply(parent.transform);
+ parent=parent.parent;
+ }
+ //get the bbox of the mesh (local coordinates)
+ var bbox=mesh.computeBoundingBox();
+ //transform the local bounding box corner coordinates to
+ //world coordinates for bounding sphere determination
+ //BBox.min
+ corner.set(bbox.min);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ //BBox.max
+ corner.set(bbox.max);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ //remaining six BBox corners
+ corner.set(bbox.min.x, bbox.max.y, bbox.max.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.min.x, bbox.min.y, bbox.max.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.min.x, bbox.max.y, bbox.min.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.max.x, bbox.min.y, bbox.min.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.max.x, bbox.min.y, bbox.max.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.max.x, bbox.max.y, bbox.min.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ }
+ //compute the smallest enclosing bounding sphere
+ mb.build();
+ //
+ //current camera settings
+ //
+ var camera=scene.cameras.getByIndex(0);
+ var res=''; //initialize result string
+ //aperture angle of the virtual camera (perspective projection) *or*
+ //orthographic scale (orthographic projection)
+ if(camera.projectionType==camera.TYPE_PERSPECTIVE){
+ var aac=camera.fov*180/Math.PI;
+ if(host.util.printf('%.4f', aac)!=30)
+ res+=host.util.printf('\n3Daac=%s,', aac);
+ }else{
+ camera.viewPlaneSize=2.*mb.radius();
+ res+=host.util.printf('\n3Dortho=%s,', 1./camera.viewPlaneSize);
+ }
+ //camera roll
+ var roll = camera.roll*180/Math.PI;
+ if(host.util.printf('%.4f', roll)!=0)
+ res+=host.util.printf('\n3Droll=%s,',roll);
+ //target to camera vector
+ var c2c=new Vector3();
+ c2c.set(camera.position);
+ c2c.subtractInPlace(camera.targetPosition);
+ c2c.normalize();
+ if(!(c2c.x==0 && c2c.y==-1 && c2c.z==0))
+ res+=host.util.printf('\n3Dc2c=%s %s %s,', c2c.x, c2c.y, c2c.z);
+ //
+ //new camera settings
+ //
+ //bounding sphere centre --> new camera target
+ var coo=new Vector3();
+ coo.set((mb.center())[0], (mb.center())[1], (mb.center())[2]);
+ if(coo.length)
+ res+=host.util.printf('\n3Dcoo=%s %s %s,', coo.x, coo.y, coo.z);
+ //radius of orbit
+ if(camera.projectionType==camera.TYPE_PERSPECTIVE){
+ var roo=mb.radius()/ Math.sin(aac * Math.PI/ 360.);
+ }else{
+ //orthographic projection
+ var roo=mb.radius();
+ }
+ res+=host.util.printf('\n3Droo=%s,', roo);
+ //update camera settings in the viewer
+ var currol=camera.roll;
+ camera.targetPosition.set(coo);
+ camera.position.set(coo.add(c2c.scale(roo)));
+ camera.roll=currol;
+ //determine background colour
+ rgb=scene.background.getColor();
+ if(!(rgb.r==1 && rgb.g==1 && rgb.b==1))
+ res+=host.util.printf('\n3Dbg=%s %s %s,', rgb.r, rgb.g, rgb.b);
+ //determine lighting scheme
+ switch(scene.lightScheme){
+ case scene.LIGHT_MODE_FILE:
+ curlights='Artwork';break;
+ case scene.LIGHT_MODE_NONE:
+ curlights='None';break;
+ case scene.LIGHT_MODE_WHITE:
+ curlights='White';break;
+ case scene.LIGHT_MODE_DAY:
+ curlights='Day';break;
+ case scene.LIGHT_MODE_NIGHT:
+ curlights='Night';break;
+ case scene.LIGHT_MODE_BRIGHT:
+ curlights='Hard';break;
+ case scene.LIGHT_MODE_RGB:
+ curlights='Primary';break;
+ case scene.LIGHT_MODE_BLUE:
+ curlights='Blue';break;
+ case scene.LIGHT_MODE_RED:
+ curlights='Red';break;
+ case scene.LIGHT_MODE_CUBE:
+ curlights='Cube';break;
+ case scene.LIGHT_MODE_CAD:
+ curlights='CAD';break;
+ case scene.LIGHT_MODE_HEADLAMP:
+ curlights='Headlamp';break;
+ }
+ if(curlights!='Artwork')
+ res+=host.util.printf('\n3Dlights=%s,', curlights);
+ //determine global render mode
+ switch(scene.renderMode){
+ case scene.RENDER_MODE_BOUNDING_BOX:
+ currender='BoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX:
+ currender='TransparentBoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX_OUTLINE:
+ currender='TransparentBoundingBoxOutline';break;
+ case scene.RENDER_MODE_VERTICES:
+ currender='Vertices';break;
+ case scene.RENDER_MODE_SHADED_VERTICES:
+ currender='ShadedVertices';break;
+ case scene.RENDER_MODE_WIREFRAME:
+ currender='Wireframe';break;
+ case scene.RENDER_MODE_SHADED_WIREFRAME:
+ currender='ShadedWireframe';break;
+ case scene.RENDER_MODE_SOLID:
+ currender='Solid';break;
+ case scene.RENDER_MODE_TRANSPARENT:
+ currender='Transparent';break;
+ case scene.RENDER_MODE_SOLID_WIREFRAME:
+ currender='SolidWireframe';break;
+ case scene.RENDER_MODE_TRANSPARENT_WIREFRAME:
+ currender='TransparentWireframe';break;
+ case scene.RENDER_MODE_ILLUSTRATION:
+ currender='Illustration';break;
+ case scene.RENDER_MODE_SOLID_OUTLINE:
+ currender='SolidOutline';break;
+ case scene.RENDER_MODE_SHADED_ILLUSTRATION:
+ currender='ShadedIllustration';break;
+ case scene.RENDER_MODE_HIDDEN_WIREFRAME:
+ currender='HiddenWireframe';break;
+ }
+ if(currender!='Solid')
+ res+=host.util.printf('\n3Drender=%s,', currender);
+ //write result string to the console
+ host.console.show();
+// host.console.clear();
+ host.console.println('%%\n%% Copy and paste the following text to the\n'+
+ '%% option list of \\includemedia!\n%%' + res + '\n');
+}
+
+function get3Dview () {
+ var camera=scene.cameras.getByIndex(0);
+ var coo=camera.targetPosition;
+ var c2c=camera.position.subtract(coo);
+ var roo=c2c.length;
+ c2c.normalize();
+ var res='VIEW%=insert optional name here\n';
+ if(!(coo.x==0 && coo.y==0 && coo.z==0))
+ res+=host.util.printf(' COO=%s %s %s\n', coo.x, coo.y, coo.z);
+ if(!(c2c.x==0 && c2c.y==-1 && c2c.z==0))
+ res+=host.util.printf(' C2C=%s %s %s\n', c2c.x, c2c.y, c2c.z);
+ if(roo > 1e-9)
+ res+=host.util.printf(' ROO=%s\n', roo);
+ var roll = camera.roll*180/Math.PI;
+ if(host.util.printf('%.4f', roll)!=0)
+ res+=host.util.printf(' ROLL=%s\n', roll);
+ if(camera.projectionType==camera.TYPE_PERSPECTIVE){
+ var aac=camera.fov * 180/Math.PI;
+ if(host.util.printf('%.4f', aac)!=30)
+ res+=host.util.printf(' AAC=%s\n', aac);
+ }else{
+ if(host.util.printf('%.4f', camera.viewPlaneSize)!=1)
+ res+=host.util.printf(' ORTHO=%s\n', 1./camera.viewPlaneSize);
+ }
+ rgb=scene.background.getColor();
+ if(!(rgb.r==1 && rgb.g==1 && rgb.b==1))
+ res+=host.util.printf(' BGCOLOR=%s %s %s\n', rgb.r, rgb.g, rgb.b);
+ switch(scene.lightScheme){
+ case scene.LIGHT_MODE_FILE:
+ curlights='Artwork';break;
+ case scene.LIGHT_MODE_NONE:
+ curlights='None';break;
+ case scene.LIGHT_MODE_WHITE:
+ curlights='White';break;
+ case scene.LIGHT_MODE_DAY:
+ curlights='Day';break;
+ case scene.LIGHT_MODE_NIGHT:
+ curlights='Night';break;
+ case scene.LIGHT_MODE_BRIGHT:
+ curlights='Hard';break;
+ case scene.LIGHT_MODE_RGB:
+ curlights='Primary';break;
+ case scene.LIGHT_MODE_BLUE:
+ curlights='Blue';break;
+ case scene.LIGHT_MODE_RED:
+ curlights='Red';break;
+ case scene.LIGHT_MODE_CUBE:
+ curlights='Cube';break;
+ case scene.LIGHT_MODE_CAD:
+ curlights='CAD';break;
+ case scene.LIGHT_MODE_HEADLAMP:
+ curlights='Headlamp';break;
+ }
+ if(curlights!='Artwork')
+ res+=' LIGHTS='+curlights+'\n';
+ switch(scene.renderMode){
+ case scene.RENDER_MODE_BOUNDING_BOX:
+ defaultrender='BoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX:
+ defaultrender='TransparentBoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX_OUTLINE:
+ defaultrender='TransparentBoundingBoxOutline';break;
+ case scene.RENDER_MODE_VERTICES:
+ defaultrender='Vertices';break;
+ case scene.RENDER_MODE_SHADED_VERTICES:
+ defaultrender='ShadedVertices';break;
+ case scene.RENDER_MODE_WIREFRAME:
+ defaultrender='Wireframe';break;
+ case scene.RENDER_MODE_SHADED_WIREFRAME:
+ defaultrender='ShadedWireframe';break;
+ case scene.RENDER_MODE_SOLID:
+ defaultrender='Solid';break;
+ case scene.RENDER_MODE_TRANSPARENT:
+ defaultrender='Transparent';break;
+ case scene.RENDER_MODE_SOLID_WIREFRAME:
+ defaultrender='SolidWireframe';break;
+ case scene.RENDER_MODE_TRANSPARENT_WIREFRAME:
+ defaultrender='TransparentWireframe';break;
+ case scene.RENDER_MODE_ILLUSTRATION:
+ defaultrender='Illustration';break;
+ case scene.RENDER_MODE_SOLID_OUTLINE:
+ defaultrender='SolidOutline';break;
+ case scene.RENDER_MODE_SHADED_ILLUSTRATION:
+ defaultrender='ShadedIllustration';break;
+ case scene.RENDER_MODE_HIDDEN_WIREFRAME:
+ defaultrender='HiddenWireframe';break;
+ }
+ if(defaultrender!='Solid')
+ res+=' RENDERMODE='+defaultrender+'\n';
+
+ //detect existing Clipping Plane (3D Cross Section)
+ var clip=null;
+ if(
+ clip=scene.nodes.getByName('$$$$$$')||
+ clip=scene.nodes.getByName('Clipping Plane')
+ );
+ for(var i=0;i<scene.nodes.count;i++){
+ var nd=scene.nodes.getByIndex(i);
+ if(nd==clip||nd.name=='') continue;
+ var ndUTFName='';
+ for (var j=0; j<nd.name.length; j++) {
+ var theUnicode = nd.name.charCodeAt(j).toString(16);
+ while (theUnicode.length<4) theUnicode = '0' + theUnicode;
+ ndUTFName += theUnicode;
+ }
+ var end=nd.name.lastIndexOf('.');
+ if(end>0) var ndUserName=nd.name.substr(0,end);
+ else var ndUserName=nd.name;
+ respart=' PART='+ndUserName+'\n';
+ respart+=' UTF16NAME='+ndUTFName+'\n';
+ defaultvals=true;
+ if(!nd.visible){
+ respart+=' VISIBLE=false\n';
+ defaultvals=false;
+ }
+ if(nd.opacity<1.0){
+ respart+=' OPACITY='+nd.opacity+'\n';
+ defaultvals=false;
+ }
+ if(nd.constructor.name=='Mesh'){
+ currender=defaultrender;
+ switch(nd.renderMode){
+ case scene.RENDER_MODE_BOUNDING_BOX:
+ currender='BoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX:
+ currender='TransparentBoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX_OUTLINE:
+ currender='TransparentBoundingBoxOutline';break;
+ case scene.RENDER_MODE_VERTICES:
+ currender='Vertices';break;
+ case scene.RENDER_MODE_SHADED_VERTICES:
+ currender='ShadedVertices';break;
+ case scene.RENDER_MODE_WIREFRAME:
+ currender='Wireframe';break;
+ case scene.RENDER_MODE_SHADED_WIREFRAME:
+ currender='ShadedWireframe';break;
+ case scene.RENDER_MODE_SOLID:
+ currender='Solid';break;
+ case scene.RENDER_MODE_TRANSPARENT:
+ currender='Transparent';break;
+ case scene.RENDER_MODE_SOLID_WIREFRAME:
+ currender='SolidWireframe';break;
+ case scene.RENDER_MODE_TRANSPARENT_WIREFRAME:
+ currender='TransparentWireframe';break;
+ case scene.RENDER_MODE_ILLUSTRATION:
+ currender='Illustration';break;
+ case scene.RENDER_MODE_SOLID_OUTLINE:
+ currender='SolidOutline';break;
+ case scene.RENDER_MODE_SHADED_ILLUSTRATION:
+ currender='ShadedIllustration';break;
+ case scene.RENDER_MODE_HIDDEN_WIREFRAME:
+ currender='HiddenWireframe';break;
+ //case scene.RENDER_MODE_DEFAULT:
+ // currender='Default';break;
+ }
+ if(currender!=defaultrender){
+ respart+=' RENDERMODE='+currender+'\n';
+ defaultvals=false;
+ }
+ }
+ if(origtrans[nd.name]&&!nd.transform.isEqual(origtrans[nd.name])){
+ var lvec=nd.transform.transformDirection(new Vector3(1,0,0));
+ var uvec=nd.transform.transformDirection(new Vector3(0,1,0));
+ var vvec=nd.transform.transformDirection(new Vector3(0,0,1));
+ respart+=' TRANSFORM='
+ +lvec.x+' '+lvec.y+' '+lvec.z+' '
+ +uvec.x+' '+uvec.y+' '+uvec.z+' '
+ +vvec.x+' '+vvec.y+' '+vvec.z+' '
+ +nd.transform.translation.x+' '
+ +nd.transform.translation.y+' '
+ +nd.transform.translation.z+'\n';
+ defaultvals=false;
+ }
+ respart+=' END\n';
+ if(!defaultvals) res+=respart;
+ }
+ if(clip){
+ var centre=clip.transform.translation;
+ var normal=clip.transform.transformDirection(new Vector3(0,0,1));
+ res+=' CROSSSECT\n';
+ if(!(centre.x==0 && centre.y==0 && centre.z==0))
+ res+=host.util.printf(
+ ' CENTER=%s %s %s\n', centre.x, centre.y, centre.z);
+ if(!(normal.x==1 && normal.y==0 && normal.z==0))
+ res+=host.util.printf(
+ ' NORMAL=%s %s %s\n', normal.x, normal.y, normal.z);
+ res+=host.util.printf(
+ ' VISIBLE=%s\n', clip.visible);
+ res+=host.util.printf(
+ ' PLANECOLOR=%s %s %s\n', clip.material.emissiveColor.r,
+ clip.material.emissiveColor.g, clip.material.emissiveColor.b);
+ res+=host.util.printf(
+ ' OPACITY=%s\n', clip.opacity);
+ res+=host.util.printf(
+ ' INTERSECTIONCOLOR=%s %s %s\n',
+ clip.wireframeColor.r, clip.wireframeColor.g, clip.wireframeColor.b);
+ res+=' END\n';
+// for(var propt in clip){
+// console.println(propt+':'+clip[propt]);
+// }
+ }
+ res+='END\n';
+ host.console.show();
+// host.console.clear();
+ host.console.println('%%\n%% Add the following VIEW section to a file of\n'+
+ '%% predefined views (See option "3Dviews"!).\n%%\n' +
+ '%% The view may be given a name after VIEW=...\n' +
+ '%% (Remove \'%\' in front of \'=\'.)\n%%');
+ host.console.println(res + '\n');
+}
+
+//add items to 3D context menu
+runtime.addCustomMenuItem("dfltview", "Generate Default View", "default", 0);
+runtime.addCustomMenuItem("currview", "Get Current View", "default", 0);
+runtime.addCustomMenuItem("csection", "Cross Section", "checked", 0);
+
+//menu event handlers
+menuEventHandler = new MenuEventHandler();
+menuEventHandler.onEvent = function(e) {
+ switch(e.menuItemName){
+ case "dfltview": calc3Dopts(); break;
+ case "currview": get3Dview(); break;
+ case "csection":
+ addremoveClipPlane(e.menuItemChecked);
+ break;
+ }
+};
+runtime.addEventHandler(menuEventHandler);
+
+//global variable taking reference to currently selected node;
+var target=null;
+selectionEventHandler=new SelectionEventHandler();
+selectionEventHandler.onEvent=function(e){
+ if(e.selected&&e.node.name!=''){
+ target=e.node;
+ }else{
+ target=null;
+ }
+}
+runtime.addEventHandler(selectionEventHandler);
+
+cameraEventHandler=new CameraEventHandler();
+cameraEventHandler.onEvent=function(e){
+ var clip=null;
+ runtime.removeCustomMenuItem("csection");
+ runtime.addCustomMenuItem("csection", "Cross Section", "checked", 0);
+ if(clip=scene.nodes.getByName('$$$$$$')|| //predefined
+ scene.nodes.getByName('Clipping Plane')){ //added via context menu
+ runtime.removeCustomMenuItem("csection");
+ runtime.addCustomMenuItem("csection", "Cross Section", "checked", 1);
+ }
+ if(clip){//plane in predefined views must be rotated by 90 deg around normal
+ clip.transform.rotateAboutLineInPlace(
+ Math.PI/2,clip.transform.translation,
+ clip.transform.transformDirection(new Vector3(0,0,1))
+ );
+ }
+ for(var i=0; i<rot4x4.length; i++){rot4x4[i].setIdentity()}
+ target=null;
+}
+runtime.addEventHandler(cameraEventHandler);
+
+var rot4x4=new Array(); //keeps track of spin and tilt axes transformations
+//key event handler for scaling moving, spinning and tilting objects
+keyEventHandler=new KeyEventHandler();
+keyEventHandler.onEvent=function(e){
+ var backtrans=new Matrix4x4();
+ var trgt=null;
+ if(target) {
+ trgt=target;
+ var backtrans=new Matrix4x4();
+ var trans=trgt.transform;
+ var parent=trgt.parent;
+ while(parent.transform){
+ //build local to world transformation matrix
+ trans.multiplyInPlace(parent.transform);
+ //also build world to local back-transformation matrix
+ backtrans.multiplyInPlace(parent.transform.inverse.transpose);
+ parent=parent.parent;
+ }
+ backtrans.transposeInPlace();
+ }else{
+ if(
+ trgt=scene.nodes.getByName('$$$$$$')||
+ trgt=scene.nodes.getByName('Clipping Plane')
+ ) var trans=trgt.transform;
+ }
+ if(!trgt) return;
+
+ var tname=trgt.name;
+ if(typeof(rot4x4[tname])=='undefined') rot4x4[tname]=new Matrix4x4();
+ if(target)
+ var tiltAxis=rot4x4[tname].transformDirection(new Vector3(0,1,0));
+ else
+ var tiltAxis=trans.transformDirection(new Vector3(0,1,0));
+ var spinAxis=rot4x4[tname].transformDirection(new Vector3(0,0,1));
+
+ //get the centre of the mesh
+ if(target&&trgt.constructor.name=='Mesh'){
+ var centre=trans.transformPosition(trgt.computeBoundingBox().center);
+ }else{ //part group (Node3 parent node, clipping plane)
+ var centre=new Vector3(trans.translation);
+ }
+ switch(e.characterCode){
+ case 30://tilt up
+ rot4x4[tname].rotateAboutLineInPlace(
+ -Math.PI/900,rot4x4[tname].translation,tiltAxis);
+ trans.rotateAboutLineInPlace(-Math.PI/900,centre,tiltAxis);
+ break;
+ case 31://tilt down
+ rot4x4[tname].rotateAboutLineInPlace(
+ Math.PI/900,rot4x4[tname].translation,tiltAxis);
+ trans.rotateAboutLineInPlace(Math.PI/900,centre,tiltAxis);
+ break;
+ case 28://spin right
+ if(e.ctrlKeyDown&&target){
+ trans.rotateAboutLineInPlace(-Math.PI/900,centre,spinAxis);
+ }else{
+ rot4x4[tname].rotateAboutLineInPlace(
+ -Math.PI/900,rot4x4[tname].translation,new Vector3(0,0,1));
+ trans.rotateAboutLineInPlace(-Math.PI/900,centre,new Vector3(0,0,1));
+ }
+ break;
+ case 29://spin left
+ if(e.ctrlKeyDown&&target){
+ trans.rotateAboutLineInPlace(Math.PI/900,centre,spinAxis);
+ }else{
+ rot4x4[tname].rotateAboutLineInPlace(
+ Math.PI/900,rot4x4[tname].translation,new Vector3(0,0,1));
+ trans.rotateAboutLineInPlace(Math.PI/900,centre,new Vector3(0,0,1));
+ }
+ break;
+ case 120: //x
+ translateTarget(trans, new Vector3(1,0,0), e);
+ break;
+ case 121: //y
+ translateTarget(trans, new Vector3(0,1,0), e);
+ break;
+ case 122: //z
+ translateTarget(trans, new Vector3(0,0,1), e);
+ break;
+ case 88: //shift + x
+ translateTarget(trans, new Vector3(-1,0,0), e);
+ break;
+ case 89: //shift + y
+ translateTarget(trans, new Vector3(0,-1,0), e);
+ break;
+ case 90: //shift + z
+ translateTarget(trans, new Vector3(0,0,-1), e);
+ break;
+ case 115: //s
+ trans.translateInPlace(centre.scale(-1));
+ trans.scaleInPlace(1.01);
+ trans.translateInPlace(centre.scale(1));
+ break;
+ case 83: //shift + s
+ trans.translateInPlace(centre.scale(-1));
+ trans.scaleInPlace(1/1.01);
+ trans.translateInPlace(centre.scale(1));
+ break;
+ }
+ trans.multiplyInPlace(backtrans);
+}
+runtime.addEventHandler(keyEventHandler);
+
+//translates object by amount calculated from Canvas size
+function translateTarget(t, d, e){
+ var cam=scene.cameras.getByIndex(0);
+ if(cam.projectionType==cam.TYPE_PERSPECTIVE){
+ var scale=Math.tan(cam.fov/2)
+ *cam.targetPosition.subtract(cam.position).length
+ /Math.min(e.canvasPixelWidth,e.canvasPixelHeight);
+ }else{
+ var scale=cam.viewPlaneSize/2
+ /Math.min(e.canvasPixelWidth,e.canvasPixelHeight);
+ }
+ t.translateInPlace(d.scale(scale));
+}
+
+function addremoveClipPlane(chk) {
+ var curTrans=getCurTrans();
+ var clip=scene.createClippingPlane();
+ if(chk){
+ //add Clipping Plane and place its center either into the camera target
+ //position or into the centre of the currently selected mesh node
+ var centre=new Vector3();
+ if(target){
+ var trans=target.transform;
+ var parent=target.parent;
+ while(parent.transform){
+ trans=trans.multiply(parent.transform);
+ parent=parent.parent;
+ }
+ if(target.constructor.name=='Mesh'){
+ var centre=trans.transformPosition(target.computeBoundingBox().center);
+ }else{
+ var centre=new Vector3(trans.translation);
+ }
+ target=null;
+ }else{
+ centre.set(scene.cameras.getByIndex(0).targetPosition);
+ }
+ clip.transform.setView(
+ new Vector3(0,0,0), new Vector3(1,0,0), new Vector3(0,1,0));
+ clip.transform.translateInPlace(centre);
+ }else{
+ if(
+ scene.nodes.getByName('$$$$$$')||
+ scene.nodes.getByName('Clipping Plane')
+ ){
+ clip.remove();clip=null;
+ }
+ }
+ restoreTrans(curTrans);
+ return clip;
+}
+
+//function to store current transformation matrix of all nodes in the scene
+function getCurTrans() {
+ var tA=new Array();
+ for(var i=0; i<scene.nodes.count; i++){
+ var nd=scene.nodes.getByIndex(i);
+ if(nd.name=='') continue;
+ tA[nd.name]=new Matrix4x4(nd.transform);
+ }
+ return tA;
+}
+
+//function to restore transformation matrices given as arg
+function restoreTrans(tA) {
+ for(var i=0; i<scene.nodes.count; i++){
+ var nd=scene.nodes.getByIndex(i);
+ if(tA[nd.name]) nd.transform.set(tA[nd.name]);
+ }
+}
+
+//store original transformation matrix of all mesh nodes in the scene
+var origtrans=getCurTrans();
+
+//set initial state of "Cross Section" menu entry
+cameraEventHandler.onEvent(1);
+
+//host.console.clear();
diff --git a/macros/latex/contrib/media9/javascript/3Dspintool.js b/macros/latex/contrib/media9/javascript/3Dspintool.js
new file mode 100644
index 0000000000..529288ad0c
--- /dev/null
+++ b/macros/latex/contrib/media9/javascript/3Dspintool.js
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) 2012, Alexander Grahn
+//
+// 3Dspintool.js
+//
+// version 20120301
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 3D JavaScript to be used with media9.sty (option `add3Djscript')
+//
+// enables the Spin tool (also accessible via 3D toolbar or context menu)
+// upon activation of the 3D scene; the scene then rotates around the upright
+// axis while dragging with the mouse
+//
+// This work may be distributed and/or modified under the
+// conditions of the LaTeX Project Public License.
+//
+// The latest version of this license is in
+// http://mirrors.ctan.org/macros/latex/base/lppl.txt
+//
+// This work has the LPPL maintenance status `maintained'.
+//
+// The Current Maintainer of this work is A. Grahn.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+runtime.setCurrentTool(runtime.TOOL_NAME_SPIN);
diff --git a/macros/latex/contrib/media9/javascript/animation.js b/macros/latex/contrib/media9/javascript/animation.js
new file mode 100644
index 0000000000..4f202c4e7d
--- /dev/null
+++ b/macros/latex/contrib/media9/javascript/animation.js
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) 2012, Alexander Grahn
+//
+// animation.js
+//
+// version 20120301
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// JavaScript for use with `add3DJScript' option of \includemedia
+//
+// * Activates keyframe animation embedded in the u3d file.
+// * Arrow keys `Down', `Up' can be used for speeding up and
+// slowing down a running animation, key `Home' for reverting
+// to the default speed.
+//
+// * Adjustable parameters:
+var rate = 1; // 1 --> use original speed as default
+var palindrome = true; // true --> play forth and back
+//
+// This work may be distributed and/or modified under the
+// conditions of the LaTeX Project Public License.
+//
+// The latest version of this license is in
+// http://mirrors.ctan.org/macros/latex/base/lppl.txt
+//
+// This work has the LPPL maintenance status `maintained'.
+//
+// The Current Maintainer of this work is A. Grahn.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//get the first animation in the scene
+var myAnim = scene.animations.getByIndex(0);
+myAnim.wallTime = 0;
+myAnim.speed = 1;
+myAnim.myLength = myAnim.endTime - myAnim.startTime;
+scene.activateAnimation(myAnim);
+
+//method to set animation speed
+myAnim.setSpeed = function (speed) {
+ speed = Math.abs(speed);
+ this.wallTime /= speed/this.speed; //correct the walltime
+ this.speed = speed;
+};
+
+//method to change animation speed by a factor
+myAnim.changeSpeed = function (mult) {
+ this.wallTime /= mult; //correct the walltime
+ this.speed *= mult;
+};
+
+//set default speed
+myAnim.setSpeed(rate);
+
+//menu items
+runtime.addCustomMenuItem("faster", "Faster (Key Up)", "default", 0);
+runtime.addCustomMenuItem("slower", "Slower (Key Down)", "default", 0);
+runtime.addCustomMenuItem("default", "Default Speed (Key Home)", "default", 0);
+
+//menu handler to control speed
+menuEventHandler = new MenuEventHandler();
+menuEventHandler.onEvent = function(e) {
+ if (e.menuItemName == "faster") {
+ myAnim.changeSpeed(1.25);
+ }
+ else if (e.menuItemName == "slower") {
+ myAnim.changeSpeed(1/1.25);
+ }
+ else if (e.menuItemName == "default") {
+ myAnim.setSpeed(rate);
+ }
+};
+runtime.addEventHandler(menuEventHandler);
+
+//key handler to control speed
+keyEventHandler = new KeyEventHandler();
+keyEventHandler.onKeyDown = true;
+keyEventHandler.onEvent = function(e) {
+ switch(e.characterCode) {
+ case 30: //key up
+ myAnim.changeSpeed(1.05);
+ break;
+
+ case 31: //key down
+ myAnim.changeSpeed(1/1.05);
+ break;
+
+ case 4: //key home
+ myAnim.setSpeed(rate);
+ break;
+
+ case 1: //key end
+ myAnim.setSpeed(rate);
+ break;
+ }
+};
+runtime.addEventHandler(keyEventHandler);
+
+//run the animation using a TimeEventHandler
+myTimer = new TimeEventHandler();
+myTimer.onTimeChange = true;
+myTimer.onEvent = function(e) {
+ myAnim.wallTime += e.deltaTime;
+ if (palindrome == true) {
+ myAnim.currentTime =
+ myAnim.startTime
+ + myAnim.myLength/2
+ * (1 - Math.cos(Math.PI * myAnim.speed/myAnim.myLength * myAnim.wallTime));
+ } else {
+ myAnim.currentTime = myAnim.startTime
+ + (myAnim.speed * myAnim.wallTime % myAnim.myLength);
+ }
+};
+runtime.addEventHandler(myTimer);
diff --git a/macros/latex/contrib/media9/javascript/asylabels.js b/macros/latex/contrib/media9/javascript/asylabels.js
new file mode 100644
index 0000000000..95f1c3c3d1
--- /dev/null
+++ b/macros/latex/contrib/media9/javascript/asylabels.js
@@ -0,0 +1,85 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) 2012, Michail Vidiassov, John C. Bowman, Alexander Grahn
+//
+// asylabels.js
+//
+// version 20120912
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 3D JavaScript to be used with media9.sty (option `add3Djscript') for
+// Asymptote generated PRC files
+//
+// adds billboard behaviour to text labels in Asymptote PRC files so that
+// they always face the camera under 3D rotation.
+//
+//
+// This work may be distributed and/or modified under the
+// conditions of the LaTeX Project Public License.
+//
+// The latest version of this license is in
+// http://mirrors.ctan.org/macros/latex/base/lppl.txt
+//
+// This work has the LPPL maintenance status `maintained'.
+//
+// The Current Maintainer of this work is A. Grahn.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+var bbnodes=new Array(); // billboard meshes
+var bbtrans=new Array(); // billboard transforms
+
+function fulltransform(mesh)
+{
+ var t=new Matrix4x4(mesh.transform);
+ if(mesh.parent.name != "") {
+ var parentTransform=fulltransform(mesh.parent);
+ t.multiplyInPlace(parentTransform);
+ return t;
+ } else
+ return t;
+}
+
+// find all text labels in the scene and determine pivoting points
+var nodes=scene.nodes;
+var nodescount=nodes.count;
+var third=1.0/3.0;
+for(var i=0; i < nodescount; i++) {
+ var node=nodes.getByIndex(i);
+ var name=node.name;
+ var end=name.lastIndexOf(".")-1;
+ if(end > 0) {
+ if(name.charAt(end) == "\001") {
+ var start=name.lastIndexOf("-")+1;
+ if(end > start) {
+ node.name=name.substr(0,start-1);
+ var nodeMatrix=fulltransform(node.parent);
+ var c=nodeMatrix.translation; // position
+ var d=Math.pow(Math.abs(nodeMatrix.determinant),third); // scale
+ bbnodes.push(node);
+ bbtrans.push(Matrix4x4().scale(d,d,d).translate(c).multiply(nodeMatrix.inverse));
+ }
+ }
+ }
+}
+
+var camera=scene.cameras.getByIndex(0);
+var zero=new Vector3(0,0,0);
+var bbcount=bbnodes.length;
+
+// event handler to maintain camera-facing text labels
+billboardHandler=new RenderEventHandler();
+billboardHandler.onEvent=function(event)
+{
+ var T=new Matrix4x4();
+ T.setView(zero,camera.position.subtract(camera.targetPosition),
+ camera.up.subtract(camera.position));
+
+ for(var j=0; j < bbcount; j++)
+ bbnodes[j].transform.set(T.multiply(bbtrans[j]));
+ runtime.refresh();
+}
+runtime.addEventHandler(billboardHandler);
+
+runtime.refresh();
diff --git a/macros/latex/contrib/media9/media9.sty b/macros/latex/contrib/media9/media9.sty
new file mode 100644
index 0000000000..d32f880d5b
--- /dev/null
+++ b/macros/latex/contrib/media9/media9.sty
@@ -0,0 +1,2385 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% media9.sty
+%
+% multimedia inclusion package
+%
+% Copyright 2012--\today, Alexander Grahn
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This package implements Adobe-Reader 9 and X compatible RichMedia
+% annotation for multimedia (Flash & 3D) inclusion according to:
+%
+% Adobe Supplement to ISO 32000, BaseVersion 1.7, ExtensionLevel 3
+%
+% Supported workflows:
+%
+% pdflatex, lualatex
+% latex-->dvips-->ps2pdf or Distiller
+% latex-->dvipdfmx
+% xelatex
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License.
+%
+% The latest version of this license is in
+% http://mirrors.ctan.org/macros/latex/base/lppl.txt
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is A. Grahn.
+
+%process non-detectable dvipdfmx driver for passing to expl3
+\RequirePackage{xkeyval}
+\DeclareOptionX{dvipdfmx}{%
+ \PassOptionsToPackage{dvipdfmx}{pdfbase}
+ \PassOptionsToPackage{driver=dvipdfmx}{expl3}
+}
+\DeclareOptionX*{}\ProcessOptionsX*\relax %allow anything as an option
+
+\RequirePackage{expl3}
+\RequirePackage{l3keys2e}
+\RequirePackage{xparse}
+\RequirePackage{atbegshi}
+
+\def\g@mix@date@tl{2019/07/11}
+\def\g@mix@version@tl{1.02}
+
+\ProvidesExplPackage{media9}{\g@mix@date@tl}{\g@mix@version@tl}
+{acrobat-9/X compatible media}
+
+\tl_gset_eq:NN\g_mix_date_tl\g@mix@date@tl
+\tl_gset_eq:NN\g_mix_version_tl\g@mix@version@tl
+
+%package options
+
+%unknown package option error message
+\msg_set:nnnn{media9}{unknown~package~option}{Unknown~package~option~`#1'.}{
+ Package option~'#1'~is~unknown;\\
+ perhaps~it~is~spelled~incorrectly.
+}
+
+\bool_new:N\g_mix_pkgattach_bool
+\bool_new:N\g_mix_dvipdfmx_bool
+\bool_new:N\g_mix_pkgdraft_bool
+\bool_new:N\g_mix_opt_pkgwindowed_bool
+\int_new:N\g_mix_pkgresizeflag_int% resizing flag according to options given
+\tl_gset:Nn\g_mix_pkgwdarg_tl{\width}
+\tl_gset:Nn\g_mix_pkghtarg_tl{\height}
+\tl_gset:Nn\g_mix_pkgttarg_tl{\totalheight}
+\bool_new:N\g_mix_pkgiso_bool
+\tl_gset:Nn\g_mix_pkgscalearg_tl{1.0}
+\tl_gset:Nx\g_mix_pkgwinsize_tl{defaultxdefault}
+\tl_gset:Nx\g_mix_pkgwinpos_tl{cc}
+\tl_gset:Nn\g_mix_pkgact_tl{/XA}
+\tl_gset:Nn\g_mix_pkgdeact_tl{/PC}
+\tl_gset:Nn\g_mix_pkgtransp_tl{false}
+\tl_gset:Nn\g_mix_pkgcontextclick_tl{false}
+\tl_gset:Nn\g_mix_pkgplaycnt_tl{-1}
+\tl_gset:Nn\g_mix_pkgplayspd_tl{1}
+\tl_gset:Nn\g_mix_pkgplaytpe_tl{None}
+\tl_gset:Nn\g_mix_pkgtools_tl{false}
+\tl_gset:Nn\g_mix_pkgnrdflt_tl{/NR~true}
+\tl_gset:Nn\g_mix_pkgnav_tl{false}
+\tl_gclear:N\g_mix_pkgopt_bg_tl
+\tl_gclear:N\g_mix_pkgopt_ls_tl
+\tl_gclear:N\g_mix_pkgopt_rm_tl
+\bool_new:N\g_mix_pkgiiidcalc_bool
+\tl_gset:Nn\g_mix_pkgpbtn_tl{fancy}
+
+%floating window settings
+\group_begin:
+\char_set_catcode_other:n{`\@}
+\cs_new_protected:Npn\mix_parse_windowedarg:w#1@#2@#3\q_stop{
+ \tl_if_blank:nF{#1}{\tl_gset:Nn\g_mix_winsize_tl{#1}}
+ \tl_if_blank:nF{#2}{\tl_gset:Nn\g_mix_winpos_tl{#2}}
+}
+\cs_new_protected:Nn\mix_parse_windowedarg:N{
+ \exp_after:wN\mix_parse_windowedarg:w#1@@\q_stop
+}
+\group_end:
+
+\cs_new:Npn\mix_parse_winsizearg:w#1x#2\q_stop{
+ \str_if_eq:nnTF{#1}{default}{
+ \tl_gset:Nx\tl_mix_fltwd{
+ \fp_to_int:n{1.522*\dim_to_decimal_in_bp:n{\g_mix_wd_tl}}}
+ \tl_gset:Nx\tl_mix_fltht{
+ \fp_to_int:n{1.522*\dim_to_decimal_in_bp:n{\g_mix_tt_tl}}}
+ }{
+ \tl_gset:Nn\tl_mix_fltwd{#1}
+ \tl_gset:Nn\tl_mix_fltht{#2}
+ }
+}
+
+\keys_define:nn{media9}{
+ xetex .code:n = {}, %dummy
+
+ dvipdfmx .code:n = {\bool_gset_true:N\g_mix_dvipdfmx_bool},
+
+ bigfiles .choice:,
+ bigfiles / true .code:n = {
+ \@ifpackageloaded{pdfbase}{
+ \bool_gset_true:N\g_pbs_pkgbigfiles_bool
+ }{
+ \PassOptionsToPackage{bigfiles}{pdfbase}
+ }
+ },
+ bigfiles / false .code:n = {},
+ bigfiles .default:n = {true},
+
+ draft .choice:,
+ draft / true .code:n = {\bool_gset_true:N\g_mix_pkgdraft_bool},
+ draft / false .code:n = {\bool_gset_false:N\g_mix_pkgdraft_bool},
+ draft .default:n = {true},
+
+ final .choice:,
+ final / true .code:n = {\bool_gset_false:N\g_mix_pkgdraft_bool},
+ final / false .code:n = {\bool_gset_true:N\g_mix_pkgdraft_bool},
+ final .default:n = {true},
+
+ attachfiles .choice:,
+ attachfiles / true .code:n = {\bool_gset_true:N\g_mix_pkgattach_bool},
+ attachfiles / false .code:n = {\bool_gset_false:N\g_mix_pkgattach_bool},
+ attachfiles .default:n = {true},
+
+ noplaybutton .choice:,
+ noplaybutton / true .code:n = {\tl_gset:Nn\g_mix_pkgpbtn_tl{none}},
+ noplaybutton .default:n = {true},
+
+ playbutton .choice:,
+ playbutton / fancy .code:n = {\tl_gset:Nn\g_mix_pkgpbtn_tl{fancy}},
+ playbutton / plain .code:n = {\tl_gset:Nn\g_mix_pkgpbtn_tl{plain}},
+ playbutton / none .code:n = {\tl_gset:Nn\g_mix_pkgpbtn_tl{none}},
+ playbutton .default:n = {fancy},
+
+ activate .choice:,
+ activate / pagevisible .code:n = {\tl_gset:Nn\g_mix_pkgact_tl{/PV}},
+ activate / pageopen .code:n = {\tl_gset:Nn\g_mix_pkgact_tl{/PO}},
+ activate / onclick .code:n = {\tl_gset:Nn\g_mix_pkgact_tl{/XA}},
+ activate .value_required:n = {true},
+
+ deactivate .choice:,
+ deactivate / pageinvisible .code:n = {\tl_gset:Nn\g_mix_pkgdeact_tl{/PI}},
+ deactivate / pageclose .code:n = {\tl_gset:Nn\g_mix_pkgdeact_tl{/PC}},
+ deactivate / onclick .code:n = {\tl_gset:Nn\g_mix_pkgdeact_tl{/XD}},
+ deactivate .value_required:n = {true},
+
+ transparent .choices:nn = {true,false}{
+ \tl_gset:Nx\g_mix_pkgtransp_tl{\l_keys_choice_tl}
+ },
+ transparent .default:n = {true},
+
+ passcontext .choices:nn = {true,false}{
+ \tl_gset:Nx\g_mix_pkgcontextclick_tl{\l_keys_choice_tl}
+ },
+ passcontext .default:n = {true},
+
+ windowed .code:n = {
+ \str_if_eq:nnTF{#1}{false}{
+ \bool_gset_false:N\g_mix_opt_pkgwindowed_bool
+ }{
+ \bool_gset_true:N\g_mix_opt_pkgwindowed_bool
+ \tl_set:Nx\l_tmpa_tl{#1}
+ \tl_remove_all:Nn\l_tmpa_tl{~}
+ \mix_parse_windowedarg:N\l_tmpa_tl
+ \tl_gset_eq:NN\g_mix_pkgwinsize_tl\g_mix_winsize_tl
+ \tl_gset_eq:NN\g_mix_pkgwinpos_tl\g_mix_winpos_tl
+ }
+ },
+
+ width .code:n = {
+ \tl_gset:Nn\g_mix_pkgwdarg_tl{#1}
+ \tl_if_exist:NF\l_mix_pkgwd_tl{
+ \int_gadd:Nn\g_mix_pkgresizeflag_int{4}
+ \tl_set:Nn\l_mix_pkgwd_tl{}
+ }
+ },
+ width .value_required:n = {true},
+
+ height .code:n = {
+ \tl_gset:Nn\g_mix_pkghtarg_tl{#1}
+ \tl_if_exist:NF\l_mix_pkght_tl{
+ \int_gadd:Nn\g_mix_pkgresizeflag_int{2}
+ \tl_set:Nn\l_mix_pkght_tl{}
+ }
+ },
+ height .value_required:n = {true},
+
+ totalheight .code:n = {
+ \tl_gset:Nn\g_mix_pkgttarg_tl{#1}
+ \tl_if_exist:NF\l_mix_pkgtt_tl{
+ \int_gadd:Nn\g_mix_pkgresizeflag_int{\c_one_int}
+ \tl_set:Nn\l_mix_pkgtt_tl{}
+ }
+ },
+ height .value_required:n = {true},
+
+ depth .code:n = {
+ \msg_warning:nnnn{media9}{deprecated~option}{depth}{
+ Ignoring~`depth'~option.
+ }
+ },
+
+ keepaspectratio .choice:,
+ keepaspectratio / true .code:n = {\bool_gset_true:N\g_mix_pkgiso_bool},
+ keepaspectratio / false .code:n = {\bool_gset_false:N\g_mix_pkgiso_bool},
+ keepaspectratio .default:n = {true},
+
+ scale .code:n = {\tl_gset:Nx\g_mix_pkgscalearg_tl{#1}},
+ scale .value_required:n = {true},
+
+ %3D specific options
+ 3Dplaytype .choice:,
+ 3Dplaytype / none .code:n = {\tl_gset:Nn\g_mix_pkgplaytpe_tl{None}},
+ 3Dplaytype / linear .code:n = {\tl_gset:Nn\g_mix_pkgplaytpe_tl{Linear}},
+ 3Dplaytype / oscillating .code:n = {
+ \tl_gset:Nn\g_mix_pkgplaytpe_tl{Oscillating}},
+ 3Dplaytype .value_required:n = {true},
+
+ 3Dplaycount .code:n = {\tl_gset:Nx\g_mix_pkgplaycnt_tl{\fp_eval:n{trunc(#1)}}},
+ 3Dplaycount .value_required:n = {true},
+
+ 3Dplayspeed .tl_gset_x:N = \g_mix_pkgplayspd_tl,
+ 3Dplayspeed .value_required:n = {true},
+
+ 3Dtoolbar .choices:nn = {true,false}{
+ \tl_gset:Nx\g_mix_pkgtools_tl{\l_keys_choice_tl}
+ },
+ 3Dtoolbar .default:n = {true},
+
+ 3Dnavpane .choices:nn = {true,false}{
+ \tl_gset:Nx\g_mix_pkgnav_tl{\l_keys_choice_tl}
+ },
+ 3Dnavpane .default:n = {true},
+
+ 3Dpartsattrs .choices:nn = {keep,restore}{
+ \tl_set:Nn\l_mix_keep_tl{keep}
+ \tl_if_eq:NNTF\l_keys_choice_tl\l_mix_keep_tl{
+ \tl_gset:Nn\g_mix_pkgnrdflt_tl{/NR~false}
+ }{
+ \tl_gset:Nn\g_mix_pkgnrdflt_tl{/NR~true}
+ }
+ },
+ 3Dpartsattrs .value_required:n = {true},
+
+ 3Dmenu .bool_gset:N = \g_mix_pkgiiidcalc_bool,
+
+ 3Dbg .code:n = {
+ \tl_gset:Nx\g_mix_pkgopt_bg_tl{
+ /BG<</Subtype/SC/CS/DeviceRGB/C[#1]/EA~true>>}
+ },
+ 3Dbg .value_required:n = {true},
+
+ 3Dlights .code:n = {
+ \tl_gset:Nx\g_mix_pkgopt_ls_tl{/LS<</Subtype/#1>>}
+ },
+ 3Dlights .value_required:n = {true},
+
+ 3Drender .code:n = {
+ \tl_gset:Nx\g_mix_pkgopt_rm_tl{/RM<</Subtype/#1>>}
+ },
+ 3Drender .value_required:n = {true},
+
+ unknown .code:n = {
+ \msg_error:nnx{media9}{unknown~package~option}{\l_keys_key_tl}
+ }
+}
+\ProcessKeysOptions{media9}
+\sys_if_engine_xetex:T{\bool_gset_true:N\g_mix_dvipdfmx_bool}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\RequirePackage{pdfbase}
+%aliases for commands from pdfbase.sty
+\cs_set_eq:NN\mix_pdfobj:nnn \pbs_pdfobj:nnn
+\cs_set_eq:NN\mix_pdfannot:nnnn\pbs_pdfannot:nnnn
+\cs_set_eq:NN\mix_appendtofields:n\pbs_appendtofields:n
+\cs_set_eq:NN\mix_pdflink:nn \pbs_pdflink:nn
+\cs_new:Nn\mix_pdfxform:n{\pbs_pdfxform:nnnnn{1}{1}{}{}{#1}}
+\cs_set_eq:NN\mix_pdfximage:n \pbs_pdfximage:n
+\cs_set_eq:NN\mix_pdfcatalog:n \pbs_pdfcatalog:n
+
+\cs_set_eq:NN\mix_pdflastobj: \pbs_pdflastobj:
+\cs_set_eq:NN\mix_pdflastann: \pbs_pdflastann:
+\cs_set_eq:NN\mix_pdflastxform: \pbs_pdflastxform:
+\cs_set_eq:NN\mix_pdflastximage:\pbs_pdflastximage:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\RequirePackage{ocgbase} %OCG generating and configuration macros
+
+\box_new:N\l_mix_poster_box
+\dim_new:N\g_mix_wd_dim
+\dim_new:N\g_mix_ht_dim
+\dim_new:N\g_mix_dp_dim
+
+\int_new:N\g_mix_rmcnt_int
+\int_new:N\l_mix_viewcnt_int %counter for 3D views per annot
+
+\int_new:N\g@mix@page@int %abs. page counter (zero based)
+\int_gset:Nn\g@mix@page@int{-1}
+\AtBeginShipout{\int_gincr:N\g@mix@page@int}
+
+\bool_new:N\l_mix_url_bool
+\bool_new:N\g_mix_iiidcalc_bool
+
+%missing package error message
+%example usage:
+% \msg_error:nnnn{media9}{missing~package}{graphicx}{[dvipdfmx]}
+% \msg_error:nnn{media9}{missing~package}{graphicx}
+\msg_set:nnn{media9}{missing~package}{
+ Package~`#1'~has~not~been~loaded~yet.\\
+ Put~the~line\\
+ ~~\string\usepackage#2{#1}\\
+ to~the~preamble~of~your~document.
+}
+
+%file not found error message
+%example usage:
+% \msg_error:nnn{media9}{file~not~found}{myfile.swf}
+\msg_set:nnnn{media9}{file~not~found}{
+ Line~\msg_line_number: :~
+ File~`#1'~not~found.}{Make~sure~file~`#1'~exists~and~is~readable!}
+
+\sys_if_engine_pdftex:TF{
+ %\pdfmdfivesum is used for hashing object references of embedded files
+ \cs_new:Nn\mix_filemdfivesum:n{\pdfmdfivesum~file~{#1}}
+ \cs_new:Nn\mix_unescapehex:n{(\pdfunescapehex{#1})}
+}{
+ \sys_if_engine_luatex:TF{
+ \RequirePackage{pdftexcmds}
+ \cs_new_nopar:Nn\mix_filemdfivesum:n{\pdf@filemdfivesum{#1}}
+ %\cs_new:Nn\mix_unescapehex:n{<#1>}
+ \cs_new_nopar:Nn\mix_unescapehex:n{(\pdf@unescapehex{#1})}
+ }{
+ \cs_if_exist:NTF\mdfivesum{
+ \cs_new_nopar:Nn\mix_filemdfivesum:n{\mdfivesum file {#1}}
+ }{
+ \cs_new_nopar:Nn\mix_filemdfivesum:n{#1}
+ }
+ }
+}
+
+%creating global definitions
+\cs_new:Npn\mix@newkey#1#2{\tl_gset:cx{#1}{#2}}
+\AtBeginDocument{
+ \iow_now:Nx\@mainaux{
+ \token_to_str:N\providecommand\token_to_str:N\mix@newkey[2]{}}
+}
+
+%macros for writing global defs to \jobname.aux
+\msg_set:nnn{media9}{rerun}{Rerun~to~get~internal~references~right!}
+\msg_set:nnn{media9}{undefined~reference}{
+ Line~\msg_line_number: :~Media~reference~`#1'~not~defined.
+}
+\msg_set:nnn{media9}{undefined~references}{
+ There~were~undefined~media~references!}
+\msg_set:nnn{media9}{same~label}{
+ Line~\msg_line_number: :~Label~`#1'~multiply~defined.
+}
+\msg_set:nnn{media9}{multiple~labels}{There~were~multiply-defined~labels!}
+
+\cs_new_nopar:Nn\mix_keytoaux_now:nn{
+ \iow_now:Nx\@mainaux{\token_to_str:N\mix@newkey{#1}{#2}}
+ \bool_if:nT{
+ !\cs_if_exist:cTF{#1}{
+ \str_if_eq_p:ee{\tl_use:c{#1}}{#2}
+ }{
+ \c_false_bool
+ }
+ }{
+ \cs_if_exist:NF\g_mix_rerunwarned_tl{
+ \tl_new:N\g_mix_rerunwarned_tl
+ \AtEndDocument{\msg_warning:nn{media9}{rerun}}
+ }
+ }
+}
+\cs_new_nopar:Nn\mix_keytoaux_shipout:nn{
+ \iow_shipout_x:Nx\@mainaux{\token_to_str:N\mix@newkey{#1}{#2}}
+ \cs_if_exist:cF{#1}{
+ \cs_if_exist:NF\g_mix_rerunwarned_tl{
+ \tl_new:N\g_mix_rerunwarned_tl
+ \AtEndDocument{\msg_warning:nn{media9}{rerun}}
+ }
+ }
+}
+
+%reset various variables for every new media inclusion
+\cs_new:Nn\mix_reset:{
+ \tl_gset:Nx\g_mix_label_tl{rm@\int_use:N\g_mix_rmcnt_int}
+ \tl_gclear:N\g_mix_usrlabel_tl
+ \tl_gclear:N\g_mix_flashvars_tl
+ \tl_gclear:N\g_mix_assets_tl
+ \tl_gclear:N\g_mix_configurations_tl
+ \tl_gset_eq:NN\g_mix_act_tl\g_mix_pkgact_tl
+ \tl_gset_eq:NN\g_mix_deact_tl\g_mix_pkgdeact_tl
+ \box_clear:N\l_mix_poster_box
+ \tl_clear:N\l_mix_scripts_tl
+ \bool_gset_eq:NN\g_mix_usrdraft_bool\g_mix_pkgdraft_bool
+ \bool_gset_eq:NN\g_mix_usrattach_bool\g_mix_pkgattach_bool
+ \tl_gset_eq:NN\g_mix_usrpbtn_tl\g_mix_pkgpbtn_tl
+ \seq_gclear:N\g_mix_res_seq
+ \seq_gclear:N\g_mix_script_seq
+ \int_gset_eq:NN\g_mix_resizeflag_int\g_mix_pkgresizeflag_int
+ \tl_set_eq:NN\l_mix_usrwd_tl\l_mix_pkgwd_tl
+ \tl_set_eq:NN\l_mix_usrht_tl\l_mix_pkght_tl
+ \tl_set_eq:NN\l_mix_usrtt_tl\l_mix_pkgtt_tl
+ \tl_gset_eq:NN\g_mix_wdarg_tl\g_mix_pkgwdarg_tl
+ \tl_gset_eq:NN\g_mix_htarg_tl\g_mix_pkghtarg_tl
+ \tl_gset_eq:NN\g_mix_ttarg_tl\g_mix_pkgttarg_tl
+ \bool_gset_eq:NN\g_mix_usriso_bool\g_mix_pkgiso_bool
+ \tl_gset_eq:NN\g_mix_scalearg_tl\g_mix_pkgscalearg_tl
+ \tl_gset_eq:NN\g_mix_transp_tl\g_mix_pkgtransp_tl
+ \tl_gset_eq:NN\g_mix_contextclick_tl\g_mix_pkgcontextclick_tl
+ \bool_gset_eq:NN\g_mix_opt_windowed_bool\g_mix_opt_pkgwindowed_bool
+ \tl_gset_eq:NN\g_mix_winsize_tl\g_mix_pkgwinsize_tl
+ \tl_gset_eq:NN\g_mix_winpos_tl\g_mix_pkgwinpos_tl
+
+ %3D specific settings
+ \bool_gset_false:N\g_mix_iiid_bool
+ \bool_gset_false:N\g_mix_opt_iiidview_bool
+ \bool_gset_eq:NN\g_mix_iiidcalc_bool\g_mix_pkgiiidcalc_bool
+
+ \tl_gset_eq:NN\g_mix_playcnt_tl\g_mix_pkgplaycnt_tl
+ \tl_gset_eq:NN\g_mix_playspd_tl\g_mix_pkgplayspd_tl
+ \tl_gset_eq:NN\g_mix_playtpe_tl\g_mix_pkgplaytpe_tl
+ \tl_gset_eq:NN\g_mix_tools_tl\g_mix_pkgtools_tl
+ \tl_gset_eq:NN\g_mix_nrdflt_tl\g_mix_pkgnrdflt_tl
+ \tl_gset_eq:NN\g_mix_nav_tl\g_mix_pkgnav_tl
+
+ \tl_gset_eq:NN\g_mix_opt_bg_tl\g_mix_pkgopt_bg_tl
+ \tl_gset_eq:NN\g_mix_opt_ls_tl\g_mix_pkgopt_ls_tl
+ \tl_gset_eq:NN\g_mix_opt_rm_tl\g_mix_pkgopt_rm_tl
+ \tl_gset:Nn\g_mix_opt_psob_tl{Min}
+ \tl_gset:Nn\g_mix_opt_coo_tl{0~0~0}
+ \tl_gset:Nn\g_mix_opt_ctoc_tl{0~-1~0}
+ \tl_gclear:N\g_mix_opt_ciiw_tl
+ \fp_gset:Nn\g_mix_opt_roo_fp{1e-9}
+ \fp_gset:Nn\g_mix_opt_aac_fp{30}
+ \fp_gset:Nn\g_mix_opt_oscale_fp{1}
+ \fp_gset:Nn\g_mix_opt_roll_fp{0}
+
+ \bool_set_false:N\l_mix_roo_bool
+ \bool_gset_false:N\g_mix_opt_ciiw_bool
+ \bool_gset_false:N\g_mix_opt_iiidortho_bool
+
+ \tl_clear:N\l_mix_iiidviewarray_tl
+ \tl_gclear:N\g_mix_vfile_tl
+}
+
+%3D settings to be reset before creating an additional view
+\cs_new:Nn\mix_iiidreset:{
+ \tl_clear:N\l_mix_bg_tl
+ \tl_clear:N\l_mix_ls_tl
+ \tl_clear:N\l_mix_rm_tl
+ \tl_set:Nn\l_mix_psob_tl{Min}
+ \tl_set:Nn\l_mix_coo_tl{0~0~0}
+ \tl_set:Nn\l_mix_ctoc_tl{0~-1~0}
+ \tl_clear:N\l_mix_ciiw_tl
+
+ \fp_set:Nn\l_mix_roo_fp{1e-9}
+ \fp_set:Nn\l_mix_aac_fp{30}
+ \fp_set:Nn\l_mix_oscale_fp{1}
+ \fp_set:Nn\l_mix_roll_fp{0}
+
+ \bool_set_false:N\l_mix_roo_bool
+ \bool_set_false:N\l_mix_ciiw_bool
+ \bool_set_false:N\l_mix_iiidortho_bool
+
+ \tl_clear:N\l_mix_naarray_tl %array of node dicts
+ \tl_clear:N\l_mix_saarray_tl %array of crosssect dicts
+ \tl_set_eq:NN\l_mix_nr_tl\g_mix_nrdflt_tl %keep or restore part attributes
+}
+
+%wrong image resource for 3D
+\msg_set:nnn{media9}{wrong~image~resource}{
+ Image~resource~file\\~~'#1'\\has~wrong~type.\\\\
+ In~3D~context,~driver~#2~only~accepts~files~of~type\\#3\\
+ as~image~resources.
+}
+
+\cs_new:Nn\mix_insert_btnocg:{%OCG for play button overlay
+ \ocgbase_new_ocg:nnn{MediaPlayButton\int_use:N\g_mix_rmcnt_int}{
+ /Print<</PrintState/OFF>>/Export<</ExportState/OFF>>
+ }{off}
+ \tl_gset:Nx\g_mix_btnocg_tl{\ocgbase_last_ocg:}
+}
+
+%output mode dependent command definitions
+\sys_if_output_pdf:TF{
+ \cs_new:Nn\mix_ndname:n{%#1:HEX encoded string
+ %converted to binary UTF16BE using \pdfunescapehex (pdfTeX only)
+ \tl_set:Nn\l_mix_ndname_tl{\mix_unescapehex:n{#1}}
+ }
+}{
+ \bool_if:NTF\g_mix_dvipdfmx_bool{ %dvipdfmx/XeTeX
+ \cs_new:Nn\mix_ndname:n{%#1:HEX encoded UTF16BE string
+ \tl_set:Nn\l_mix_ndname_tl{<#1>}
+ }
+ }{
+ %3DNode name
+ \cs_new:Nn\mix_ndname:n{%#1:HEX encoded unicode string
+ \special{ps:~
+ /mix@unicode~65535~string~def~
+ (#1)~/ASCIIHexDecode~filter~mix@unicode~readstring~pop~
+ /mix@unicode~exch~def~
+ }
+ %converted to binary UTF16BE by ps2pdf
+ \tl_set:Nn\l_mix_ndname_tl{mix@unicode}
+ }
+ }
+}
+
+\cs_new:Nn\mix_embedasset:n{ %#1 file name or url
+ %check if #1 is online resource
+ \bool_set_false:N\l_mix_url_bool\tl_set:Nx\l_tmpa_tl{#1}
+ \regex_match:nVT{(^[Hh][Tt][Tt][Pp][Ss]?|^[Ff][Tt][Pp]):\/\/}\l_tmpa_tl{
+ \bool_set_true:N\l_mix_url_bool
+ }
+ \filename@parse{#1}
+ \tl_set:Nx\l_mix_base_tl{\filename@base}
+ \tl_set:Nx\l_mix_ext_tl{\tl_lower_case:n{\filename@ext}}
+ \bool_if:NTF\l_mix_url_bool{
+ \cs_if_exist:cF{url_#1}{
+ \mix_pdfobj:nnn{}{dict}{/FS/URL/F(#1)}
+ \tl_gset:cx{url_#1}{\mix_pdflastobj:}
+ }
+ %every new url should be added to assets tree of the current annot
+ \cs_if_exist:cF{url_\int_use:c{g_mix_rmcnt_int}_#1}{
+ \tl_gput_right:Nx\g_mix_assets_tl{~(#1)~\tl_use:c{url_#1}}
+ \tl_new:c{url_\int_use:c{g_mix_rmcnt_int}_#1}
+ }
+ \tl_gset:Nx\g_mix_lastasset_tl{\tl_use:c{url_#1}}
+ }{
+ \file_if_exist:nTF{#1}{
+ \file_get_full_name:nN{#1}\l_mix_file_tl
+ }{
+ \msg_error:nnx{media9}{file~not~found}{#1}
+ }
+ \bool_if:nTF{
+ %in 3D context, image resources must be embedded as Image XObjects, not
+ %as file streams
+ \g_mix_iiid_bool && (
+ \str_if_eq_p:Vn\l_mix_ext_tl{jpg}
+ || \str_if_eq_p:Vn\l_mix_ext_tl{jpeg}
+ || \str_if_eq_p:Vn\l_mix_ext_tl{png}
+ || \str_if_eq_p:Vn\l_mix_ext_tl{jbig2}
+ || \str_if_eq_p:Vn\l_mix_ext_tl{jb2}
+ || \str_if_eq_p:Vn\l_mix_ext_tl{ps}
+ || \str_if_eq_p:Vn\l_mix_ext_tl{eps}
+ || \str_if_eq_p:Vn\l_mix_ext_tl{pdf}
+ )
+ }{
+ \cs_if_exist:cF{imgXobj_\mix_filemdfivesum:n{\l_mix_file_tl}}{
+ \mix_pdfximage:n{\l_mix_file_tl}
+ \tl_gset:cx{imgXobj_\mix_filemdfivesum:n{\l_mix_file_tl}}{
+ \mix_pdflastximage:
+ }
+ }
+ %create fake FileSpec for current file name if not yet existent
+ \cs_if_exist:cF{ImgFileSpecFor3D_#1}{
+ \tl_gset:cx{ImgFileSpecFor3D_#1}{
+ \tl_use:c{imgXobj_\mix_filemdfivesum:n{\l_mix_file_tl}}
+ }
+ }
+ %add FileSpec to assets tree of current annot
+ \cs_if_exist:cF{fileAsset_\int_use:c{g_mix_rmcnt_int}_#1}{
+ \tl_gput_right:Nx\g_mix_assets_tl{~(#1)~\tl_use:c{ImgFileSpecFor3D_#1}}
+ \tl_new:c{fileAsset_\int_use:c{g_mix_rmcnt_int}_#1}
+ }
+ \tl_gset:Nx\g_mix_lastasset_tl{\tl_use:c{ImgFileSpecFor3D_#1}}
+ }{
+ %remaining file types in 3D context and any file type in case of Flash
+ %context will be embedded as ordinary fstreams;
+ \cs_if_exist:cF{fileEmb_\mix_filemdfivesum:n{\l_mix_file_tl}}{
+ \mix_pdfobj:nnn{}{fstream}{{}{\l_mix_file_tl}}
+ \tl_gset:cx{fileEmb_\mix_filemdfivesum:n{\l_mix_file_tl}}{
+ \mix_pdflastobj:
+ }
+ }
+ %create FileSpec for current file name if not yet existent
+ \cs_if_exist:cF{fileSpec_#1}{
+ \mix_pdfobj:nnn{}{dict}{
+ /Type/Filespec/F~(#1)%/UF~(#1)
+ /EF~<</F~\tl_use:c{fileEmb_\mix_filemdfivesum:n{\l_mix_file_tl}}>>
+ }
+ \tl_gset:cx{fileSpec_#1}{\mix_pdflastobj:}
+ \bool_if:NT\g_mix_usrattach_bool{
+ \mix_pdfannot:nnnn{0pt}{0pt}{0pt}{
+ /Contents~(media~resource)
+ /F~2\cs_if_exist_use:N
+ /Subtype/FileAttachment
+ /FS~\mix_pdflastobj:
+ }
+ }
+ }
+ %add FileSpec to assets tree of current annot
+ \cs_if_exist:cF{fileAsset_\int_use:c{g_mix_rmcnt_int}_#1}{
+ \tl_gput_right:Nx\g_mix_assets_tl{~(#1)~\tl_use:c{fileSpec_#1}}
+ \tl_new:c{fileAsset_\int_use:c{g_mix_rmcnt_int}_#1}
+ }
+ \tl_gset:Nx\g_mix_lastasset_tl{\tl_use:c{fileSpec_#1}}
+ }
+ }
+}
+
+\cs_new:Nn\mix_draftbox:n{ %#1 text string to be shown in the draft box centre
+ \hbox_overlap_right:n{
+ \hbox_to_wd:nn{\g_mix_wd_tl}{
+ \vrule~height~\g_mix_ht_tl~depth~\g_mix_dp_tl\hss
+ \vrule
+ }
+ }
+ \box_move_down:nn{\g_mix_dp_tl}{
+ \hbox_to_wd:nn{\g_mix_wd_tl}{
+ \vbox_to_ht:nn{\g_mix_tt_tl}{
+ \hrule~width~\g_mix_wd_tl\vss
+ \hbox_to_wd:nn{\g_mix_wd_tl}{\ttfamily{\tiny#1}\hss}\vss
+ \hrule
+ }
+ }
+ }
+}
+
+\msg_set:nnn{media9}{zero~size}{
+ Media~annotation~\msg_line_context:\ has~zero\\
+ size~in~at~least~one~dimension.\\\\
+ Provide~a~poster~text~with~non-zero~width~and\\
+ height,~or~specify~a~suitable~size~via~`width'\\
+ and~`height'~options.
+}
+
+\msg_set:nnn{media9}{zero~width}{
+ Media~annotation~\msg_line_context:\ has~zero~width.\\\\
+ Provide~a~poster~text~with~non-zero~width~or\\
+ set~a~valid~one~using~the~`width'~option.
+}
+
+\msg_set:nnn{media9}{zero~height}{
+ Media~annotation~\msg_line_context:\ has~zero~height.\\\\
+ Provide~a~poster~text~with~non-zero~height~or\\
+ set~a~valid~height~using~one~of~`height'~or\\
+ `totalheight'~options.
+}
+
+
+%calculates widget dimensions from natural ones, taking resizing options
+%into account
+\int_new:N\g_mix_resizeflag_int% resizing flags according to options given
+\cs_new:Nn\mix_scale:n{% #1 box number
+ %totalheight overrides height if both height & totalheight options were given
+ \bool_if:nT{
+ \int_compare_p:n{\g_mix_resizeflag_int=3} ||
+ \int_compare_p:n{\g_mix_resizeflag_int=7}
+ }{\int_gsub:Nn\g_mix_resizeflag_int{2}}
+ \group_begin:
+ %natural dimensions \width, \height, \depth, \totalheight
+ \tl_set:Nn\width {\box_wd:N#1}
+ \tl_set:Nn\height{\box_ht:N#1}
+ \tl_set:Nn\depth {\box_dp:N#1}
+ \tl_set:Nn\totalheight{\dimexpr\height+\depth\relax}
+ \tl_gset:Nx\g_tmpa_tl{\dim_eval:n{\width}}
+ \tl_gset:Nx\g_tmpb_tl{\dim_eval:n{\totalheight}}
+ %evaluate width/height/totalheight options
+ \tl_gset:Nx\g_mix_wd_tl{\dim_abs:n{\g_mix_wdarg_tl}}
+ \tl_gset:Nx\g_mix_ht_tl{\dim_abs:n{\g_mix_htarg_tl}}
+ \tl_gset:Nx\g_mix_tt_tl{\dim_abs:n{\g_mix_ttarg_tl}}
+ \dim_compare:nT{\width=\c_zero_dim}{\box_gset_wd:Nn#1{\g_mix_wd_tl}}
+ \dim_compare:nT{\totalheight=\c_zero_dim}{
+ \bool_if:nT{ %height option given
+ \int_compare_p:n{\g_mix_resizeflag_int=6}||
+ \int_compare_p:n{\g_mix_resizeflag_int=2}
+ }{\box_gset_ht:Nn#1{\g_mix_ht_tl}}
+ \bool_if:nT{ %totalheight option given
+ \int_compare_p:n{\g_mix_resizeflag_int=5}||
+ \int_compare_p:n{\g_mix_resizeflag_int=\c_one_int}
+ }{\box_gset_ht:Nn#1{\g_mix_tt_tl}}
+ }
+ \group_end:
+ \tl_gset:Nn\g_mix_dp_tl{\c_zero_dim} %to be initialised here
+ %now resize (originally non-zero size) poster box according to the
+ %options given
+ \bool_if:nF{
+ \dim_compare_p:n{\g_tmpa_tl=\c_zero_dim}||
+ \dim_compare_p:n{\g_tmpb_tl=\c_zero_dim}
+ }{
+ %bit 2^2=width, 2^1=height, 2^0=totalhight given
+ \int_case:nn{\g_mix_resizeflag_int}{
+ {\c_one_int}{
+ \box_resize_to_ht_plus_dp:Nn#1{\g_mix_tt_tl}
+ }
+ {2}{
+ \box_resize_to_ht:Nn#1{\g_mix_ht_tl}
+ }
+ {4}{
+ \box_resize_to_wd:Nn#1{\g_mix_wd_tl}
+ }
+ {5}{
+ \bool_if:NTF\g_mix_usriso_bool{
+ \dim_set:Nn\l_tmpa_dim{
+ (\box_ht:N#1+\box_dp:N#1)*\dim_ratio:nn{\g_mix_wd_tl}{\box_wd:N#1}
+ }
+ \dim_set:Nn\l_tmpa_dim{\dim_abs:n{\l_tmpa_dim}}
+ \dim_set:Nn\l_tmpb_dim{\dim_abs:n{\g_mix_tt_tl}}
+ \dim_compare:nTF{\l_tmpa_dim<\l_tmpb_dim}{
+ \box_resize_to_wd:Nn#1{\g_mix_wd_tl}
+ }{
+ \box_resize_to_ht_plus_dp:Nn#1{\g_mix_tt_tl}
+ }
+ }{
+ \box_resize_to_wd_and_ht_plus_dp:Nnn#1{\g_mix_wd_tl}{\g_mix_tt_tl}
+ }
+ }
+ {6}{
+ \bool_if:NTF\g_mix_usriso_bool{
+ \dim_set:Nn\l_tmpa_dim{
+ \box_ht:N#1*\dim_ratio:nn{\g_mix_wd_tl}{\box_wd:N#1}
+ }
+ \dim_set:Nn\l_tmpa_dim{\dim_abs:n{\l_tmpa_dim}}
+ \dim_set:Nn\l_tmpb_dim{\dim_abs:n{\g_mix_ht_tl}}
+ \dim_compare:nTF{\l_tmpa_dim<\l_tmpb_dim}{
+ \box_resize_to_wd:Nn#1{\g_mix_wd_tl}
+ }{
+ \box_resize_to_ht:Nn#1{\g_mix_ht_tl}
+ }
+ }{
+ \box_resize_to_wd_and_ht:Nnn#1{\g_mix_wd_tl}{\g_mix_ht_tl}
+ }
+ }
+ }
+ }
+ %apply scaling factor
+ \box_scale:Nnn#1{\g_mix_scalearg_tl}{\g_mix_scalearg_tl}
+ %dimensions after resizing
+ \tl_gset:Nx\g_mix_wd_tl{\dim_use:N\box_wd:N#1}
+ \tl_gset:Nx\g_mix_ht_tl{\dim_use:N\box_ht:N#1}
+ \tl_gset:Nx\g_mix_dp_tl{\dim_use:N\box_dp:N#1}
+ \tl_gset:Nx\g_mix_tt_tl{\dim_eval:n{\box_ht:N#1+\box_dp:N#1}}
+ \dim_compare:nT{\g_mix_wd_tl=\c_zero_dim}{\msg_warning:nn{media9}{zero~width}}
+ \dim_compare:nT{\g_mix_tt_tl=\c_zero_dim}{
+ \msg_warning:nn{media9}{zero~height}}
+}
+
+\bool_new:N\g_mix_iiid_bool
+\int_new:N\l_mix_lineno_int
+
+\NewDocumentCommand\addmediapath{m}{
+ \str_set:Nn \l_tmpa_str {#1/}
+ \seq_put_right:NV \l_file_search_path_seq \l_tmpa_str
+}
+
+%play button overlay; two versions: fancy and plain
+\box_new:N\l_mix_pbtn_box
+\ExplSyntaxOff
+\newdimen\mix@btn@dim
+\def\g@mix@pbtn@fancy@tl#1#2{% width, total height
+ \ifdefined\tikz%
+ \mix@btn@dim=\dimexpr\baselineskip*3\relax%
+ \ifdim#1<\mix@btn@dim\mix@btn@dim=#1\fi%
+ \ifdim#2<\mix@btn@dim\mix@btn@dim=#2\fi%
+ \tikz[x=\mix@btn@dim,y=\mix@btn@dim]{%
+ \shade [opacity=\ifdim#2>\dimexpr\baselineskip*6\relax0.6\else0\fi,
+ shading angle=-180,even odd rule]
+ (-\dimexpr#1/2\relax,-0.5) rectangle (\dimexpr#1/2\relax,0.5)
+ (0,0) circle (0.4);
+ \shade [ball color=gray,opacity=0.6] (0,0) circle (0.4);
+ \fill [color=white,opacity=0.6] %triangle
+ (-0.152,0.224)--(-0.152,-0.224)--+(30:0.4844)--cycle;
+ }%
+ \else%
+ \ifcsname g_mix_rerunwarned_tl\endcsname\else%
+ \expandafter\gdef\csname g_mix_rerunwarned_tl\endcsname{}%
+ \AtEndDocument{\csname msg_warning:nn\endcsname{media9}{rerun}}%
+ \fi%
+ \fi%
+ \ifdefined\mix@AtEnd@Doc\else%
+ \@bsphack\protected@write\@mainaux{}{%
+ \string\ifcsname\space mix@AtEnd@Doc\string\endcsname\string\else%
+ \string\AtBeginDocument{\string\RequirePackage{tikz}}%
+ \string\fi%
+ }\@esphack%
+ \gdef\mix@AtEnd@Doc{}%
+ \fi%
+}
+\def\g@mix@pbtn@plain@tl#1#2{% width, total height
+ \ifdefined\tikz%
+ \mix@btn@dim=\dimexpr\baselineskip*3\relax%
+ \ifdim#1<\mix@btn@dim\mix@btn@dim=#1\fi%
+ \ifdim#2<\mix@btn@dim\mix@btn@dim=#2\fi%
+ \tikz[x=\mix@btn@dim,y=\mix@btn@dim]{%
+ \fill [opacity=\ifdim#2>\dimexpr\baselineskip*6\relax0.5\else0\fi,
+ color=gray,even odd rule]
+ (-\dimexpr#1/2\relax,-0.5) rectangle (\dimexpr#1/2\relax,0.5);
+ (0,0) circle (0.4);
+ \fill [color=black,opacity=0.5] (0,0) circle (0.4);
+ \fill [color=white,opacity=0.8]
+ (-0.152,0.224)--(-0.152,-0.224)--+(30:0.4844)--cycle;
+ }%
+ \else%
+ \ifcsname g_mix_rerunwarned_tl\endcsname\else%
+ \expandafter\gdef\csname g_mix_rerunwarned_tl\endcsname{}%
+ \AtEndDocument{\csname msg_warning:nn\endcsname{media9}{rerun}}%
+ \fi%
+ \fi%
+ \ifdefined\mix@AtEnd@Doc\else%
+ \@bsphack\protected@write\@mainaux{}{%
+ \string\ifcsname\space mix@AtEnd@Doc\string\endcsname\string\else%
+ \string\AtBeginDocument{\string\RequirePackage{tikz}}%
+ \string\fi%
+ }\@esphack%
+ \gdef\mix@AtEnd@Doc{}%
+ \fi%
+}
+\ExplSyntaxOn
+
+%environment for setting LTR typesetting direction with e-TeX based engines
+\cs_new:Nn\mix_beginLTR:{
+ \cs_if_exist:NT\TeXXeTstate{
+ \int_compare:nT{\TeXXeTstate>\c_zero_int}{\beginL}
+ }
+}
+\cs_new:Nn\mix_endLTR:{
+ \cs_if_exist:NT\TeXXeTstate{
+ \int_compare:nT{\TeXXeTstate>\c_zero_int}{\endL}
+ }
+}
+
+\ior_new:N\l_mix_vfile_stream %file stream for 3D views files
+\NewDocumentCommand\includemedia{O{}mm}{%#1 options, #2 text/image #3 media file
+ \mix_uriend:
+ \group_begin:
+ \mix_beginLTR:
+ \leavevmode
+ %empty stream as appearance dummy
+ \cs_if_exist:NF\g_mix_appearance_tl{
+ \hbox_set:Nn\l_mix_poster_box{\phantom{\rule{1pt}{1pt}}}
+ \mix_pdfxform:n{\l_mix_poster_box}
+ \tl_gset:Nx\g_mix_appearance_tl{\mix_pdflastxform:}
+ }
+ \mix_reset:
+ \mix_uribegin: %treat URI characters correctly
+ \keys_set:nn{media9/user}{#1}
+ \mix_uriend:
+ \tl_greplace_all:Nnn\g_mix_flashvars_tl{~&}{&}
+ \tl_greplace_all:Nnn\g_mix_flashvars_tl{&~}{&}
+ \tl_greplace_all:Nnn\g_mix_flashvars_tl{~=}{=}
+ \tl_greplace_all:Nnn\g_mix_flashvars_tl{=~}{=}
+ \tl_gtrim_spaces:N\g_mix_flashvars_tl
+
+ \cs_if_exist:NT\Ginput@path{\cs_set_eq:NN\input@path\Ginput@path}
+
+ %\hbox_set:Nn\l_mix_poster_box{\group_begin:#2\group_end:}
+ %\hbox_set:Nn\l_mix_poster_box{#2}
+ \sbox\l_mix_poster_box{#2}
+ \mix_scale:n{\l_mix_poster_box}
+ \bool_if:nT{
+ \dim_compare_p:n{\g_mix_tt_tl=\c_zero_dim}||
+ \dim_compare_p:n{\g_mix_wd_tl=\c_zero_dim}
+ }{
+ \tl_gset:Nn\g_mix_usrpbtn_tl{none}
+ }
+ \bool_if:NTF\g_mix_usrdraft_bool{
+ \tl_if_empty:NF\g_mix_usrlabel_tl{
+ \mix_keytoaux_now:nn{ann@\g_mix_usrlabel_tl}{draft}
+ }
+ \tl_if_blank:oTF{#2}{
+ \mix_uribegin: %treat URI characters correctly
+ \mix_draftbox:n{\tl_to_str:n{#3}}
+ \mix_uriend:
+ }{
+ \hbox_to_wd:nn{\g_mix_wd_tl}{
+ \vrule~width~\c_zero_dim~height~\g_mix_ht_tl~depth~\g_mix_dp_tl
+ \box_use:N\l_mix_poster_box\hss
+ }
+ }
+ }{
+ \bool_if:nF{\str_if_eq_p:Vn\g_mix_usrpbtn_tl{none}}{
+ %attach script that switches off play button overlay upon activation
+ \mix_pdfobj:nnn{}{stream}{
+ {}{
+ var~ocgs=host.getOCGs(host.pageNum);
+ for(var~i=0;i<ocgs.length;i++){
+ if(ocgs[i].name=='MediaPlayButton\int_use:N\g_mix_rmcnt_int'){
+ ocgs[i].state=false;
+ }
+ }
+ }
+ }
+ \mix_pdfobj:nnn{}{dict}{
+ /Type/Filespec/EF~<</F~\mix_pdflastobj:>>/F~(btnoff)%/UF~(btnoff)
+ }
+ \tl_gset:Nx\g_mix_assets_tl{(btnoff)~\mix_pdflastobj:}
+ \tl_set:Nx\l_mix_scripts_tl{~\mix_pdflastobj:~}
+ %enforce plain playbutton overlay for XeTeX
+ \sys_if_engine_xetex:T{\tl_gset:Nn\g_mix_usrpbtn_tl{plain}}
+ }
+ %embed main asset
+ \mix_uribegin:
+ \mix_embedasset:n{#3}
+ \bool_if:nTF{
+ \str_if_eq_p:Vn\l_mix_ext_tl{prc}||\str_if_eq_p:Vn\l_mix_ext_tl{u3d}
+ }{
+ \bool_gset_true:N\g_mix_iiid_bool
+ \tl_gset:Nn\g_mix_mainassetsub_tl{3D}
+ }{
+ \str_case_e:nnF{\l_mix_base_tl}{
+ {APlayer}{\tl_gset:Nn\g_mix_mainassetsub_tl{Sound}}
+ {AudioPlayer}{\tl_gset:Nn\g_mix_mainassetsub_tl{Sound}}
+ {VPlayer}{\tl_gset:Nn\g_mix_mainassetsub_tl{Video}}
+ {VideoPlayer}{\tl_gset:Nn\g_mix_mainassetsub_tl{Video}}
+ {StrobeMediaPlayback}{\tl_gset:Nn\g_mix_mainassetsub_tl{Video}}
+ }{\tl_gset:Nn\g_mix_mainassetsub_tl{Flash}}
+ }
+ \tl_gset:Nx\g_mix_mainasset_tl{~\g_mix_lastasset_tl}
+ \mix_uriend:
+ %secondary assets (added by addresource)
+ \seq_map_function:NN\g_mix_res_seq\mix_embedasset:n
+ \seq_map_inline:Nn\g_mix_script_seq{
+ \mix_embedasset:n{##1}
+ \tl_put_right:Nx\l_mix_scripts_tl{~\g_mix_lastasset_tl}
+ }
+ %attach 3D calculation script
+ \bool_if:nT{\g_mix_iiid_bool && \g_mix_iiidcalc_bool}{
+ \mix_embedasset:n{3Dmenu.js}
+ \tl_put_left:Nx\l_mix_scripts_tl{\g_mix_lastasset_tl~}
+ }
+ %create 3D views from file (option 3Dviews)
+ \bool_if:nT{\g_mix_iiid_bool && !(\tl_if_blank_p:V\g_mix_vfile_tl)}{
+ \tl_set:Nn\l_mix_level_tl{-1}
+ \int_zero:N\l_mix_viewcnt_int
+ \int_zero:N\l_mix_lineno_int
+
+ \ior_open:Nn\l_mix_vfile_stream{\g_mix_vfile_tl}
+ \ior_map_inline:Nn\l_mix_vfile_stream{
+ \int_incr:N\l_mix_lineno_int
+ \keys_set:nn{media9/views}{##1} %process input line
+ }
+ \ior_close:N\l_mix_vfile_stream
+ }
+ \bool_if:nT{ %create default 3D view if required
+ \g_mix_iiid_bool &&
+ (\g_mix_opt_iiidview_bool || \tl_if_blank_p:V\l_mix_iiidviewarray_tl)
+ }{
+ \tl_set_eq:NN\l_mix_bg_tl\g_mix_opt_bg_tl
+ \tl_set_eq:NN\l_mix_ls_tl\g_mix_opt_ls_tl
+ \tl_set_eq:NN\l_mix_rm_tl\g_mix_opt_rm_tl
+ \tl_set_eq:NN\l_mix_psob_tl\g_mix_opt_psob_tl
+ \tl_set_eq:NN\l_mix_coo_tl\g_mix_opt_coo_tl
+ \tl_set_eq:NN\l_mix_ctoc_tl\g_mix_opt_ctoc_tl
+ \tl_set_eq:NN\l_mix_ciiw_tl\g_mix_opt_ciiw_tl
+
+ \fp_set_eq:NN\l_mix_roo_fp\g_mix_opt_roo_fp
+ \fp_set_eq:NN\l_mix_aac_fp\g_mix_opt_aac_fp
+ \fp_set_eq:NN\l_mix_oscale_fp\g_mix_opt_oscale_fp
+ \fp_set_eq:NN\l_mix_roll_fp\g_mix_opt_roll_fp
+
+ \bool_set_eq:NN\l_mix_ciiw_bool\g_mix_opt_ciiw_bool
+ \bool_set_eq:NN\l_mix_iiidortho_bool\g_mix_opt_iiidortho_bool
+ \tl_clear:N\l_mix_naarray_tl
+
+ \bool_if:NF\l_mix_ciiw_bool{
+ \tl_set:Nx\l_mix_args_tl{\l_mix_coo_tl\space\l_mix_ctoc_tl\space}
+ \exp_after:wN\mix_calc_ciiw:w\l_mix_args_tl\q_stop
+ }
+
+ \tl_clear:N\l_mix_naarray_tl
+ \tl_clear:N\l_mix_saarray_tl
+ \tl_set_eq:NN\l_mix_nr_tl\g_mix_nrdflt_tl
+
+ \mix_view:n{Default}
+ \tl_set:Nx\l_mix_dfltview_tl{\mix_pdflastobj:}
+
+ }
+ \mix_pdfobj:nnn{}{dict}{
+ /Type/RichMediaInstance
+ /Subtype/\g_mix_mainassetsub_tl
+ /Asset~\g_mix_mainasset_tl
+ \bool_if:NF\g_mix_iiid_bool{
+ /Params~<<
+ /Binding\str_if_eq:VnTF\g_mix_transp_tl{true}{
+ /Foreground
+ }{
+ /Background
+ }
+ \tl_if_blank:VF\g_mix_flashvars_tl{
+ /FlashVars~(\g_mix_flashvars_tl)
+ }
+ >>
+ }
+ }
+ \tl_if_empty:NF\g_mix_usrlabel_tl{
+ \mix_keytoaux_now:nn{main@\g_mix_usrlabel_tl}{\mix_pdflastobj:}
+ }
+ \mix_pdfobj:nnn{}{dict}{
+ /Type/RichMediaConfiguration
+ /Subtype/\g_mix_mainassetsub_tl
+ /Instances~[\mix_pdflastobj:]
+ }
+ \mix_pdfobj:nnn{}{dict}{
+ /Type/RichMediaContent
+ /Assets~<</Names~[\g_mix_assets_tl]>>
+ /Configurations~[\mix_pdflastobj:]
+ \bool_if:nT{
+ \g_mix_iiid_bool && !(\tl_if_empty_p:V\l_mix_iiidviewarray_tl)
+ }{
+ /Views~[\l_mix_iiidviewarray_tl]
+ }
+ }
+ \tl_set:Nx\l_mix_content_tl{\mix_pdflastobj:}
+ \bool_if:nT{
+ \g_mix_iiid_bool && \str_if_eq_p:Vn\g_mix_playtpe_tl{Oscillating}
+ }{
+ \tl_gset:Nx\g_mix_playcnt_tl{\int_eval:n{\g_mix_playcnt_tl*2}}
+ }
+ %determine floating window size
+ \bool_if:NT\g_mix_opt_windowed_bool{
+ \exp_after:wN\mix_parse_winsizearg:w\g_mix_winsize_tl\q_stop
+ }
+ \mix_pdfobj:nnn{}{dict}{
+ /Activation~<<
+ /Condition\g_mix_act_tl
+ \bool_if:nT{
+ \g_mix_iiid_bool &&
+ !(\str_if_eq_p:Vn\g_mix_playtpe_tl{None})
+ }{%seems to work only with 3D content
+ /Animation~<<
+ /Subtype/\g_mix_playtpe_tl
+ /PlayCount~\g_mix_playcnt_tl
+ /Speed~\g_mix_playspd_tl
+ >>
+ }
+ \bool_if:nT{
+ \g_mix_iiid_bool && (
+ \g_mix_opt_iiidview_bool || \tl_if_blank_p:V\l_mix_iiidviewarray_tl
+ )
+ }{/View~\l_mix_dfltview_tl}
+ /Presentation~<<
+ /Transparent~\g_mix_transp_tl
+ \bool_if:NTF\g_mix_opt_windowed_bool{
+ /Style/Windowed
+ /Window~<<
+ /Width~<</Default~\tl_mix_fltwd>>
+ /Height~<</Default~\tl_mix_fltht>>
+ /Position~<<
+ /HOffset~0/VOffset~0
+ \str_case_e:nnF{\g_mix_winpos_tl}{
+ {tl}{/VAlign/Near/HAlign/Near}
+ {cl}{/VAlign/Center/HAlign/Near}
+ {bl}{/VAlign/Far/HAlign/Near}
+ {bc}{/VAlign/Far/HAlign/Center}
+ {br}{/VAlign/Far/HAlign/Far}
+ {cr}{/VAlign/Center/HAlign/Far}
+ {tr}{/VAlign/Near/HAlign/Far}
+ {tc}{/VAlign/Near/HAlign/Center}
+ }{/HAlign/Center/VAlign/Center}
+ >>
+ >>
+ }{
+ /Style/Embedded
+ }
+ \bool_if:NTF\g_mix_iiid_bool{
+ /Toolbar~\g_mix_tools_tl
+ /NavigationPane~\g_mix_nav_tl
+ }{
+ /PassContextClick~\g_mix_contextclick_tl
+ }
+ >>
+ \tl_if_blank:VF\l_mix_scripts_tl{/Scripts~[\l_mix_scripts_tl]}
+ >>
+ /Deactivation~<</Condition\g_mix_deact_tl>>
+ }
+ \tl_set:Nx\l_mix_settings_tl{\mix_pdflastobj:}
+ \tl_if_empty:NF\g_mix_usrlabel_tl{
+ \tl_gset_eq:NN\g_mix_label_tl\g_mix_usrlabel_tl
+ }
+ % #1:width, #2:height, #3:depth, #4:content (key-value)
+ \mix_pdfannot:nnnn{\g_mix_wd_tl}{\g_mix_ht_tl}{\g_mix_dp_tl}{
+ /Subtype/RichMedia
+ /F~4
+ /BS~<</W~0/S/S>>
+ /Contents~(media~embedded~by~media9~[\g_mix_version_tl~(\g_mix_date_tl)])
+ /NM~(\g_mix_label_tl)
+ /AP~<</N~\g_mix_appearance_tl>>
+ /RichMediaSettings~\l_mix_settings_tl
+ /RichMediaContent~\l_mix_content_tl
+ }
+ \tl_if_empty:NF\g_mix_usrlabel_tl{
+ \mix_keytoaux_now:nn{ann@\g_mix_usrlabel_tl}{\mix_pdflastann:}
+ \mix_keytoaux_shipout:nn{page@\g_mix_usrlabel_tl}{
+ \noexpand\the\g@mix@page@int}
+ }
+ \mix_pdfobj:nnn{}{dict}{
+ /S/JavaScript/JS~(
+ try{
+ if(typeof(annotRM)=='undefined'){annotRM=new~Array();}
+ if(typeof(annotRM['\g_mix_label_tl'])=='undefined'){
+ annotRM['\g_mix_label_tl']=
+ this.getAnnotRichMedia(this.pageNum,'\g_mix_label_tl');
+ }
+ \str_if_eq:eeF{\g_mix_usrpbtn_tl}{none}{
+ if(typeof(ocgBtn\int_use:N\g_mix_rmcnt_int)=='undefined'){
+ var~ocgs=this.getOCGs(this.pageNum);
+ for(var~i=0;i<ocgs.length;i++){
+ if(ocgs[i].name=='MediaPlayButton\int_use:N\g_mix_rmcnt_int'){
+ var~ocgBtn\int_use:N\g_mix_rmcnt_int=ocgs[i];
+ }
+ }
+ }
+ if(
+ !annotRM['\g_mix_label_tl'].activated&&(
+ app.viewerType=='Reader'||
+ app.viewerType=='Exchange'||
+ app.viewerType=='Exchange-Pro'
+ )&&app.viewerVersion>=9
+ ){
+ ocgBtn\int_use:N\g_mix_rmcnt_int.state=true;
+ }
+ }
+ }catch(e){}
+ this.dirty=false;
+ )
+ }
+ \mix_pdfannot:nnnn{0pt}{0pt}{0pt}{
+ /Subtype/Widget
+ /FT/Btn/Ff~65537/F~2
+ /BS~<</W~0>>
+ /T~(fd@\g_mix_label_tl)
+ /AA~<</PO~\mix_pdflastobj:~/PV~\mix_pdflastobj:>>
+ }
+ \mix_appendtofields:n{\mix_pdflastann:}
+ \hbox_overlap_right:n{\box_use:N\l_mix_poster_box}
+ \str_if_eq:eeTF{\g_mix_usrpbtn_tl}{none}{
+ \hbox_to_wd:nn{\g_mix_wd_tl}{
+ \vrule~width~\c_zero_dim~height~\g_mix_ht_tl~depth~\g_mix_dp_tl\hss
+ }
+ }{%insert play button overlay
+ \mix_insert_btnocg: %create OCG
+ \cs_gset_eq:Nc\mix_pbtn:NN{g@mix@pbtn@\g_mix_usrpbtn_tl @tl}
+ \group_begin:
+ \cs_if_exist:NT\textdir{\textdir TLT}
+ \sbox\l_mix_pbtn_box{\mix_pbtn:NN\g_mix_wd_tl\g_mix_tt_tl}
+ \box_move_down:nn{\g_mix_dp_tl}{
+ \hbox_to_wd:nn{\g_mix_wd_tl}{
+ \vbox_to_ht:nn{\g_mix_tt_tl}{
+ \vss
+ \ocgbase_oc_bdc:n{\g_mix_btnocg_tl}
+ \box_use:N\l_mix_pbtn_box
+ \ocgbase_oc_emc:
+ }
+ }
+ }
+ \group_end:
+ }
+ \int_gincr:N\g_mix_rmcnt_int
+ }
+ \mix_endLTR:
+ \group_end:
+}
+\tl_set_eq:NN\l_mix_includemedia_tl\includemedia
+\tl_set:Nn\includemedia{\mix_uribegin:\l_mix_includemedia_tl}
+
+%environment \mix_uribegin: ... \mix_uriend: to sanitize possibly
+%active chars in URLs (RFC 2396), path specifications and JavaScript
+\group_begin:
+\char_set_catcode_other:n{`\~}
+\cs_new_protected_nopar:Npn\mix_uribegin:{
+ \group_begin:
+ %code contributed by J. Wright
+ \tl_map_inline:nn{.:;?!/"'*+,->=<$@([])^_`|~}{
+ \cs_set_nopar:Npx\__mix_tmp:w{\token_to_str:N##1}
+ \char_set_active_eq:NN##1\__mix_tmp:w
+ }
+ \tl_map_inline:nn{\#\&\%\\\{\}}{
+ \cs_set_nopar:Npx##1{\token_to_str:N##1}
+ }
+}
+\group_end:
+\cs_set_eq:NN\mix_uriend:\group_end:
+
+%macro for building the C2W transformation matrix
+\cs_new:Npn\mix_calc_ciiw:w#1~#2~#3~#4~#5~#6\q_stop{
+ % #1,#2,#3 centre of orbit coordinates (coo)
+ % #4,#5,#6 centre of orbit to camera direction vector (c2c)
+ %normalized view vector (opposite to c2c)
+ \fp_set:Nn\l_mix_mag_fp{(#4*#4 + #5*#5 + #6*#6)**0.5}
+ \fp_set:Nn\l_mix_viewx_fp{-#4/\l_mix_mag_fp}
+ \fp_set:Nn\l_mix_viewy_fp{-#5/\l_mix_mag_fp}
+ \fp_set:Nn\l_mix_viewz_fp{-#6/\l_mix_mag_fp}
+
+ %camera roll
+ \fp_set:Nn\l_mix_sinroll_fp{sin(\l_mix_roll_fp deg)}
+ \fp_set:Nn\l_mix_cosroll_fp{cos(\l_mix_roll_fp deg)}
+
+ %top and bottom views
+ \fp_set:Nn\l_mix_leftx_fp{-1.0}
+ \fp_set:Nn\l_mix_lefty_fp{0.0}
+ \fp_set:Nn\l_mix_leftz_fp{0.0}
+ \fp_compare:nNnTF\l_mix_viewz_fp<\c_zero_fp{% top view
+ %up-vector
+ \fp_set:Nn\l_mix_upx_fp{0.0}
+ \fp_set:Nn\l_mix_upy_fp{1.0}
+ \fp_set:Nn\l_mix_upz_fp{0.0}
+ }{% bottom view
+ %up-vector
+ \fp_set:Nn\l_mix_upx_fp{0.0}
+ \fp_set:Nn\l_mix_upy_fp{-1.0}
+ \fp_set:Nn\l_mix_upz_fp{0.0}
+ }
+ \fp_set:Nn\l_mix_sumxy_fp{abs(\l_mix_viewx_fp) + abs(\l_mix_viewy_fp)}
+ \fp_compare:nNnF\l_mix_sumxy_fp=\c_zero_fp{% other views than top and bottom
+ %up-vector = up_world - (up_world dot view) view
+ \fp_set:Nn\l_mix_upx_fp{-\l_mix_viewz_fp*\l_mix_viewx_fp}
+ \fp_set:Nn\l_mix_upy_fp{-\l_mix_viewz_fp*\l_mix_viewy_fp}
+ \fp_set:Nn\l_mix_upz_fp{-\l_mix_viewz_fp*\l_mix_viewz_fp + 1.0}
+ %normalize up-vector
+ \fp_set:Nn\l_mix_mag_fp{(\l_mix_upx_fp*\l_mix_upx_fp
+ + \l_mix_upy_fp*\l_mix_upy_fp + \l_mix_upz_fp*\l_mix_upz_fp)**0.5}
+ \fp_set:Nn\l_mix_upx_fp{\l_mix_upx_fp/\l_mix_mag_fp}
+ \fp_set:Nn\l_mix_upy_fp{\l_mix_upy_fp/\l_mix_mag_fp}
+ \fp_set:Nn\l_mix_upz_fp{\l_mix_upz_fp/\l_mix_mag_fp}
+ %left vector = up x view
+ \fp_set:Nn\l_mix_leftx_fp{
+ \l_mix_viewz_fp*\l_mix_upy_fp - \l_mix_viewy_fp*\l_mix_upz_fp}
+ \fp_set:Nn\l_mix_lefty_fp{
+ \l_mix_viewx_fp*\l_mix_upz_fp - \l_mix_viewz_fp*\l_mix_upx_fp}
+ \fp_set:Nn\l_mix_leftz_fp{
+ \l_mix_viewy_fp*\l_mix_upx_fp - \l_mix_viewx_fp*\l_mix_upy_fp}
+ %normalize left vector
+ \fp_set:Nn\l_mix_mag_fp{(\l_mix_leftx_fp*\l_mix_leftx_fp
+ + \l_mix_lefty_fp*\l_mix_lefty_fp + \l_mix_leftz_fp*\l_mix_leftz_fp)**0.5}
+ \fp_set:Nn\l_mix_leftx_fp{\l_mix_leftx_fp/\l_mix_mag_fp}
+ \fp_set:Nn\l_mix_lefty_fp{\l_mix_lefty_fp/\l_mix_mag_fp}
+ \fp_set:Nn\l_mix_leftz_fp{\l_mix_leftz_fp/\l_mix_mag_fp}
+ }
+
+ %apply camera roll
+ \fp_set:Nn\l_mix_leftxprime_fp{
+ \l_mix_leftx_fp*\l_mix_cosroll_fp + \l_mix_upx_fp*\l_mix_sinroll_fp}
+ \fp_set:Nn\l_mix_leftyprime_fp{
+ \l_mix_lefty_fp*\l_mix_cosroll_fp + \l_mix_upy_fp*\l_mix_sinroll_fp}
+ \fp_set:Nn\l_mix_leftzprime_fp{
+ \l_mix_leftz_fp*\l_mix_cosroll_fp + \l_mix_upz_fp*\l_mix_sinroll_fp}
+
+ \fp_set:Nn\l_mix_upxprime_fp{
+ \l_mix_upx_fp*\l_mix_cosroll_fp - \l_mix_leftx_fp*\l_mix_sinroll_fp}
+ \fp_set:Nn\l_mix_upyprime_fp{
+ \l_mix_upy_fp*\l_mix_cosroll_fp - \l_mix_lefty_fp*\l_mix_sinroll_fp}
+ \fp_set:Nn\l_mix_upzprime_fp{
+ \l_mix_upz_fp*\l_mix_cosroll_fp - \l_mix_leftz_fp*\l_mix_sinroll_fp}
+
+ \fp_set_eq:NN\l_mix_leftx_fp\l_mix_leftxprime_fp
+ \fp_set_eq:NN\l_mix_lefty_fp\l_mix_leftyprime_fp
+ \fp_set_eq:NN\l_mix_leftz_fp\l_mix_leftzprime_fp
+ \fp_set_eq:NN\l_mix_upx_fp\l_mix_upxprime_fp
+ \fp_set_eq:NN\l_mix_upy_fp\l_mix_upyprime_fp
+ \fp_set_eq:NN\l_mix_upz_fp\l_mix_upzprime_fp
+
+ %translation vector
+ \fp_set:Nn\l_mix_transx_fp{#1 - \l_mix_roo_fp*\l_mix_viewx_fp}
+ \fp_set:Nn\l_mix_transy_fp{#2 - \l_mix_roo_fp*\l_mix_viewy_fp}
+ \fp_set:Nn\l_mix_transz_fp{#3 - \l_mix_roo_fp*\l_mix_viewz_fp}
+
+ %format elements of transformation matrix
+ \tl_set:Nx\l_mix_leftx_tl {\fp_to_decimal:n{round((\l_mix_leftx_fp),9)} }
+ \tl_set:Nx\l_mix_lefty_tl {\fp_to_decimal:n{round((\l_mix_lefty_fp),9)} }
+ \tl_set:Nx\l_mix_leftz_tl {\fp_to_decimal:n{round((\l_mix_leftz_fp),9)} }
+ \tl_set:Nx\l_mix_upx_tl {\fp_to_decimal:n{round((\l_mix_upx_fp),9)} }
+ \tl_set:Nx\l_mix_upy_tl {\fp_to_decimal:n{round((\l_mix_upy_fp),9)} }
+ \tl_set:Nx\l_mix_upz_tl {\fp_to_decimal:n{round((\l_mix_upz_fp),9)} }
+ \tl_set:Nx\l_mix_viewx_tl {\fp_to_decimal:n{round((\l_mix_viewx_fp),9)} }
+ \tl_set:Nx\l_mix_viewy_tl {\fp_to_decimal:n{round((\l_mix_viewy_fp),9)} }
+ \tl_set:Nx\l_mix_viewz_tl {\fp_to_decimal:n{round((\l_mix_viewz_fp),9)} }
+ \tl_set:Nx\l_mix_transx_tl{\fp_to_decimal:n{round((\l_mix_transx_fp),9)}}
+ \tl_set:Nx\l_mix_transy_tl{\fp_to_decimal:n{round((\l_mix_transy_fp),9)}}
+ \tl_set:Nx\l_mix_transz_tl{\fp_to_decimal:n{round((\l_mix_transz_fp),9)}}
+
+ %concat elements to matrix
+ \tl_set:Nx\l_mix_ciiw_tl{
+ \l_mix_leftx_tl \space
+ \l_mix_lefty_tl \space
+ \l_mix_leftz_tl \space
+ \l_mix_upx_tl \space
+ \l_mix_upy_tl \space
+ \l_mix_upz_tl \space
+ \l_mix_viewx_tl \space
+ \l_mix_viewy_tl \space
+ \l_mix_viewz_tl \space
+ \l_mix_transx_tl\space
+ \l_mix_transy_tl\space
+ \l_mix_transz_tl
+ }
+}
+
+% 3D view object
+\cs_new:Nn\mix_view:n{
+ \tl_set:Nx\l_mix_oscale_tl{\fp_to_decimal:n{round((\l_mix_oscale_fp),9)}}
+ \tl_set:Nx\l_mix_aac_tl{\fp_to_decimal:n{round((\l_mix_aac_fp),9)}}
+ \tl_set:Nx\l_mix_roo_tl{\fp_to_decimal:n{round((\l_mix_roo_fp),9)}}
+ \mix_pdfobj:nnn{}{dict}{
+ /MS/M
+ /P<<
+ \bool_if:NTF\l_mix_iiidortho_bool{
+ /Subtype/O/OS~\l_mix_oscale_tl/OB/\l_mix_psob_tl
+ }{
+ /Subtype/P/FOV~\l_mix_aac_tl/PS/\l_mix_psob_tl
+ }
+ >>
+ /C2W~[\l_mix_ciiw_tl]
+ /CO~\l_mix_roo_tl
+ \l_mix_nr_tl
+ /NA~[\l_mix_naarray_tl]
+ /SA~[\l_mix_saarray_tl]
+ \l_mix_bg_tl\l_mix_ls_tl\l_mix_rm_tl
+ /XN~(#1)/IN~(#1)
+ }
+}
+
+%document command options
+
+\msg_set:nnnn{media9}{unknown~option}{
+ Line~\msg_line_number: :~Unknown~option~`#1'.
+}{
+ Option~'#1'~is~not~known~by~media9:\\
+ perhaps~it~is~spelled~incorrectly.
+}
+
+\msg_set:nnn{media9}{deprecated~option}{
+ Line~\msg_line_number: :~Option~`#1'~deprecated.\\
+ #2
+}
+
+\bool_new:N\g_mix_usrdraft_bool
+\bool_new:N\g_mix_usriso_bool
+\bool_new:N\g_mix_usrattach_bool
+\bool_new:N\g_mix_usrpbtn_bool
+\bool_new:N\g_mix_opt_iiidview_bool
+\bool_new:N\l_mix_roo_bool
+\bool_new:N\g_mix_opt_ciiw_bool
+\bool_new:N\g_mix_opt_iiidortho_bool
+\bool_new:N\l_mix_ciiw_bool
+\bool_new:N\l_mix_iiidortho_bool
+\bool_new:N\g_mix_opt_windowed_bool
+
+\seq_new:N\g_mix_res_seq
+\seq_new:N\g_mix_script_seq
+\tl_new:N\g_mix_rmnames_tl
+
+\keys_define:nn{media9/user}{
+ %user override automatic label
+ label .code:n = {
+ \tl_gset:Nx\g_mix_usrlabel_tl{#1}
+ \tl_gtrim_spaces:N\g_mix_usrlabel_tl
+ \cs_if_exist:cTF{rm@#1}{
+ \msg_warning:nnx{media9}{same~label}{#1}
+ \cs_if_exist:NF\g_mix_samelabel_tl{
+ \tl_new:N\g_mix_samelabel_tl
+ \AtEndDocument{\msg_warning:nn{media9}{multiple~labels}}
+ }
+ }{
+ \tl_new:c{rm@#1}
+ }
+ },
+ label .value_required:n = {true},
+
+ width .code:n = {
+ \tl_gset:Nn\g_mix_wdarg_tl{#1}
+ \tl_if_exist:NF\l_mix_usrwd_tl{
+ \int_gadd:Nn\g_mix_resizeflag_int{4}
+ \tl_set:Nn\l_mix_usrwd_tl{}
+ }
+ },
+ width .value_required:n = {true},
+
+ height .code:n = {
+ \tl_gset:Nn\g_mix_htarg_tl{#1}
+ \tl_if_exist:NF\l_mix_usrht_tl{
+ \int_gadd:Nn\g_mix_resizeflag_int{2}
+ \tl_set:Nn\l_mix_usrht_tl{}
+ }
+ },
+ height .value_required:n = {true},
+
+ totalheight .code:n = {
+ \tl_gset:Nn\g_mix_ttarg_tl{#1}
+ \tl_if_exist:NF\l_mix_usrtt_tl{
+ \int_gadd:Nn\g_mix_resizeflag_int{\c_one_int}
+ \tl_set:Nn\l_mix_usrtt_tl{}
+ }
+ },
+ height .value_required:n = {true},
+
+ depth .code:n = {
+ \msg_warning:nnnn{media9}{deprecated~option}{depth}{
+ Ignoring~`depth'~option.
+ }
+ },
+
+ keepaspectratio .choice:,
+ keepaspectratio / true .code:n = {\bool_gset_true:N\g_mix_usriso_bool},
+ keepaspectratio / false .code:n = {\bool_gset_false:N\g_mix_usriso_bool},
+ keepaspectratio .default:n = {true},
+
+ scale .code:n = {\tl_gset:Nx\g_mix_scalearg_tl{#1}},
+ scale .value_required:n = {true},
+
+ url .code:n = {
+ \msg_warning:nnnn{media9}{deprecated~option}{url}{
+ Instead,~use~a~fully~qualified~URL~starting~with~`http://'~or~`ftp://'.
+ }
+ },
+
+ addresource .code:n = {\seq_gput_right:Nn\g_mix_res_seq{#1}},
+ addresource .value_required:n = {true},
+
+ add3Djscript .code:n = {\seq_gput_right:Nn\g_mix_script_seq{#1}},
+ add3Djscript .value_required:n = {true},
+
+ flashvars .tl_gset_x:N = \g_mix_flashvars_tl,
+ flashvars .value_required:n = {true},
+
+ activate .choice:,
+ activate / pagevisible .code:n = {\tl_gset:Nn\g_mix_act_tl{/PV}},
+ activate / pageopen .code:n = {\tl_gset:Nn\g_mix_act_tl{/PO}},
+ activate / onclick .code:n = {\tl_gset:Nn\g_mix_act_tl{/XA}},
+ activate .value_required:n = {true},
+
+ deactivate .choice:,
+ deactivate / pageinvisible .code:n = {\tl_gset:Nn\g_mix_deact_tl{/PI}},
+ deactivate / pageclose .code:n = {\tl_gset:Nn\g_mix_deact_tl{/PC}},
+ deactivate / onclick .code:n = {\tl_gset:Nn\g_mix_deact_tl{/XD}},
+ deactivate .value_required:n = {true},
+
+ draft .choice:,
+ draft / true .code:n = {\bool_gset_true:N\g_mix_usrdraft_bool},
+ draft / false .code:n = {\bool_gset_false:N\g_mix_usrdraft_bool},
+ draft .default:n = {true},
+
+ final .choice:,
+ final / true .code:n = {\bool_gset_false:N\g_mix_usrdraft_bool},
+ final / false .code:n = {\bool_gset_true:N\g_mix_usrdraft_bool},
+ final .default:n = {true},
+
+ attachfiles .choice:,
+ attachfiles / true .code:n = {\bool_gset_true:N\g_mix_usrattach_bool},
+ attachfiles / false .code:n = {\bool_gset_false:N\g_mix_usrattach_bool},
+ attachfiles .default:n = {true},
+
+ noplaybutton .choice:,
+ noplaybutton / true .code:n = {\tl_gset:Nn\g_mix_usrpbtn_tl{none}},
+ noplaybutton .default:n = {true},
+
+ playbutton .choice:,
+ playbutton / fancy .code:n = {\tl_gset:Nn\g_mix_usrpbtn_tl{fancy}},
+ playbutton / plain .code:n = {\tl_gset:Nn\g_mix_usrpbtn_tl{plain}},
+ playbutton / none .code:n = {\tl_gset:Nn\g_mix_usrpbtn_tl{none}},
+ playbutton .default:n = {fancy},
+
+ transparent .choices:nn = {true,false}{
+ \tl_gset:Nx\g_mix_transp_tl{\l_keys_choice_tl}
+ },
+ transparent .default:n = {true},
+
+ passcontext .choices:nn = {true,false}{
+ \tl_gset:Nx\g_mix_contextclick_tl{\l_keys_choice_tl}
+ },
+ passcontext .default:n = {true},
+
+ windowed .code:n = {
+ \str_if_eq:nnTF{#1}{false}{
+ \bool_gset_false:N\g_mix_opt_windowed_bool
+ }{
+ \bool_gset_true:N\g_mix_opt_windowed_bool
+ \tl_set:Nx\l_tmpa_tl{#1}
+ \tl_remove_all:Nn\l_tmpa_tl{~}
+ \mix_parse_windowedarg:N\l_tmpa_tl
+ }
+ },
+
+ %3D specific options
+ 3Dplaytype .choice:,
+ 3Dplaytype / none .code:n = {\tl_gset:Nn\g_mix_playtpe_tl{None}},
+ 3Dplaytype / linear .code:n = {\tl_gset:Nn\g_mix_playtpe_tl{Linear}},
+ 3Dplaytype / oscillating .code:n = {
+ \tl_gset:Nn\g_mix_playtpe_tl{Oscillating}},
+ 3Dplaytype .value_required:n = {true},
+
+ 3Dplaycount .code:n = {\tl_gset:Nx\g_mix_playcnt_tl{\fp_eval:n{trunc(#1)}}},
+ 3Dplaycount .value_required:n = {true},
+
+ 3Dplayspeed .tl_gset_x:N = \g_mix_playspd_tl,
+ 3Dplayspeed .value_required:n = {true},
+
+ 3Dtoolbar .choices:nn = {true,false}{
+ \tl_gset:Nx\g_mix_tools_tl{\l_keys_choice_tl}},
+ 3Dtoolbar .default:n = {true},
+
+ 3Dnavpane .choices:nn = {true,false}{
+ \tl_gset:Nx\g_mix_nav_tl{\l_keys_choice_tl}},
+ 3Dnavpane .default:n = {true},
+
+ 3Dpartsattrs .choices:nn = {keep,restore}{
+ \tl_set:Nn\l_mix_keep_tl{keep}
+ \tl_if_eq:NNTF\l_keys_choice_tl\l_mix_keep_tl{
+ \tl_gset:Nn\g_mix_nrdflt_tl{/NR~false}
+ }{
+ \tl_gset:Nn\g_mix_nrdflt_tl{/NR~true}
+ }
+ },
+ 3Dpartsattrs .value_required:n = {true},
+
+ 3Dcoo .code:n = {\tl_gset:Nn\g_mix_opt_coo_tl{#1}
+ \bool_gset_true:N\g_mix_opt_iiidview_bool},
+ 3Dcoo .value_required:n = {true},
+
+ 3Dc2c .code:n = {\tl_gset:Nn\g_mix_opt_ctoc_tl{#1}
+ \bool_gset_true:N\g_mix_opt_iiidview_bool},
+ 3Dc2c .value_required:n = {true},
+
+ 3Dc2w .code:n = {
+ \tl_gset:Nx\g_mix_opt_ciiw_tl{#1}
+ \bool_gset_true:N\g_mix_opt_iiidview_bool
+ \bool_gset_true:N\g_mix_opt_ciiw_bool
+ },
+ 3Dc2w .value_required:n = {true},
+
+ 3Droo .code:n = {
+ \fp_gset:Nn\g_mix_opt_roo_fp{abs(#1)}
+ \fp_compare:nNnT\g_mix_opt_roo_fp<{1e-9}{
+ \fp_gset:Nn\g_mix_opt_roo_fp{1e-9}}
+ \bool_gset_true:N\g_mix_opt_iiidview_bool
+ \bool_set_true:N\l_mix_roo_bool
+ },
+ 3Droo .value_required:n = {true},
+
+ 3Daac .code:n = {\fp_gset:Nn\g_mix_opt_aac_fp{#1}
+ \bool_gset_true:N\g_mix_opt_iiidview_bool},
+ 3Daac .value_required:n = {true},
+
+ 3Dortho .code:n = {
+ \bool_gset_true:N\g_mix_opt_iiidview_bool
+ \bool_gset_true:N\g_mix_opt_iiidortho_bool
+ \fp_compare:nF{#1=0.0}{
+ \fp_gset:Nn\g_mix_opt_oscale_fp{#1}
+ \bool_if:NF\l_mix_roo_bool{
+ \fp_set:Nn\l_mix_temp_fp{1/#1/2}
+ \fp_gset_eq:NN\g_mix_opt_roo_fp\l_mix_temp_fp
+ }
+ }
+ },
+ 3Dortho .default:n = {1},
+
+ 3Droll .code:n = {\fp_gset:Nn\g_mix_opt_roll_fp{#1}
+ \bool_gset_true:N\g_mix_opt_iiidview_bool},
+ 3Droll .value_required:n = {true},
+
+ 3Dmenu .bool_gset:N = \g_mix_iiidcalc_bool,
+
+ 3Dbg .code:n = {
+ \tl_gset:Nx\g_mix_opt_bg_tl{/BG<</Subtype/SC/CS/DeviceRGB/C[#1]/EA~true>>}
+ },
+ 3Dbg .value_required:n = {true},
+
+ 3Dlights .code:n = {
+ \tl_gset:Nx\g_mix_opt_ls_tl{/LS<</Subtype/#1>>}
+ },
+ 3Dlights .value_required:n = {true},
+
+ 3Drender .code:n = {
+ \regex_extract_once:nnNTF{
+ ^(Transparent(?:Wireframe)?):(0|0\.[0-9]*|\.[0-9]+|1\.?|1\.0*)$
+ }{#1}\l_mix_tmp_seq{
+ \seq_pop_right:NN\l_mix_tmp_seq\l_mix_alpha_tl
+ \seq_pop_right:NN\l_mix_tmp_seq\l_mix_rmode_tl
+ \tl_gset:Nx\g_mix_opt_rm_tl{
+ /RM<</Subtype/\l_mix_rmode_tl/O~\l_mix_alpha_tl>>
+ }
+ }{
+ \tl_gset:Nx\g_mix_opt_rm_tl{/RM<</Subtype/#1>>}
+ }
+ },
+ 3Drender .value_required:n = {true},
+
+ 3Dpsob .code:n = {
+ \tl_gset:Nx\g_mix_opt_psob_tl{#1}
+ },
+ 3Dpsob .value_required:n = {true},
+
+ 3Dviews .code:n = {
+ \file_if_exist:nTF{#1}{
+ \file_get_full_name:nN{#1}\l_mix_temp_tl
+ \tl_gset_eq:NN\g_mix_vfile_tl\l_mix_temp_tl
+ }{
+ \msg_error:nnx{media9}{file~not~found}{#1}
+ }
+ },
+ 3Dviews .value_required:n = {true},
+
+ unknown .code:n = {
+ \msg_error:nnx{media9}{unknown~option}{\l_keys_key_tl}
+ }
+}
+
+%3D views file parsing
+
+%messages related to 3D views file parsing
+\msg_set:nnn{media9}{nested~view}{
+ File~#1,~line~#2:\\
+ A~VIEW~section~cannot~be~nested~into~another~section.
+}
+
+\msg_set:nnn{media9}{key~not~allowed}{
+ File~#1,~line~#2:\\
+ Key~`#3'~not~allowed~here;~must~go~into~#4~section.
+}
+
+\msg_set:nnn{media9}{section~not~allowed}{
+ File~#1,~line~#2:\\
+ #3~not~allowed~here;~must~be~a~sub-section~of~a~#4~section.
+}
+
+\msg_set:nnn{media9}{missing~part~name}{
+ File~#1,~line~#2:\\
+ You~must~provide~a~valid~PART~name~(PART=<part name>),~as~displayed\\
+ in~the~model~tree~of~the~3D~object~(go~to~`View'->`Navigation~Panels'\\
+ ->`Model~Tree'~in~Adobe~Reader).
+}
+
+\msg_set:nnn{media9}{missing~utf16~name}{
+ File~#1,~line~#2:\\
+ You~must~provide~a~valid~hex~encoded~part~name~(UTF16NAME=<part~name\\
+ as~hex~encoded~UTF-16~string>).~Enable~`3Dmenu'~option~and~choose\\
+ `Get~Current~View'~from~3D~context~menu~to~generate~correct~views~file\\
+ entries.
+}
+
+\msg_set:nnn{media9}{too~many~cross~sections}{
+ File~#1,~line~#2:\\
+ Adobe~Reader~supports~only~one~cross~section~per~3D~view.\\
+ I~am~ignoring~this~CROSSSECT.
+}
+
+\msg_set:nnn{media9}{no~end~here}{
+ File~#1,~line~#2:\\
+ There~is~nothing~to~END~here.
+}
+
+\cs_new:Npn\mix_parse_vect:w#1~#2~#3\q_stop{
+ \fp_set:Nn\l_mix_x_fp{#1}
+ \fp_set:Nn\l_mix_y_fp{#2}
+ \fp_set:Nn\l_mix_z_fp{#3}
+}
+
+\cs_new:Nn\mix_arcsin:Nnnn{
+ %#1 result (rad), #2 argument, #3 current term no., #4 total number of
+ %terms
+ \int_compare:nTF{#3==\c_zero_int}{
+ \fp_set:Nn\l_mix_tmpa_fp{1.0}
+ \fp_set:Nn\l_mix_tmpb_fp{1.0}
+ \fp_set:Nn\l_mix_tmpc_fp{1.0}
+ \fp_set:Nn\l_mix_tmpd_fp{#2}
+
+ \fp_set:Nn#1{#2}
+ }{
+ \fp_set:Nn\l_mix_tmpa_fp{\l_mix_tmpa_fp * \int_eval:n{2*#3-1}}
+ \fp_set:Nn\l_mix_tmpb_fp{\l_mix_tmpb_fp * \int_eval:n{2*#3}}
+ \fp_set:Nn\l_mix_tmpc_fp{\int_eval:n{2*#3+1}}
+ \fp_set:Nn\l_mix_tmpd_fp{\l_mix_tmpd_fp * #2 * #2}
+
+ \fp_set:Nn#1{#1 + \l_mix_tmpd_fp*\l_mix_tmpa_fp/\l_mix_tmpb_fp/\l_mix_tmpc_fp}
+ }
+ \int_compare:nF{#3==#4}{
+ \mix_arcsin:Nnnn#1{#2}{\int_eval:n{#3+1}}{#4}
+ }
+}
+
+\cs_new:Nn\mix_angfromxy:Nnn{ %#1 result (deg), #2 x, #3 y
+ \fp_set:Nn\l_mix_asinarg_fp{#3/(#2*#2 + #3*#3)**0.5}
+ \fp_set:Nn\l_mix_acosarg_fp{#2/(#2*#2 + #3*#3)**0.5}
+ \fp_set:Nn\l_mix_halfsqrttwo_fp{2**0.5/2}
+ \mix_arcsin:Nnnn\l_mix_arcsin_fp{\l_mix_asinarg_fp}{0}{10}
+ \mix_arcsin:Nnnn\l_mix_arccos_fp{\l_mix_acosarg_fp}{0}{10}
+ \fp_set:Nn\l_mix_asinarg_fp{abs(\l_mix_asinarg_fp)}
+ %determine quadrant
+ \fp_compare:nTF{#2>=\c_zero_fp}{
+ \fp_compare:nTF{#3>=\c_zero_fp}{ %I
+ \fp_compare:nNnTF\l_mix_asinarg_fp<\l_mix_halfsqrttwo_fp{
+ \fp_set_eq:NN#1\l_mix_arcsin_fp
+ }{
+ \fp_set:Nn#1{\c_pi_fp/2 - \l_mix_arccos_fp}
+ }
+ }{ %IV
+ \fp_compare:nNnTF\l_mix_asinarg_fp<\l_mix_halfsqrttwo_fp{
+ \fp_set_eq:NN#1\l_mix_arcsin_fp
+ }{
+ \fp_set:Nn#1{\l_mix_arccos_fp-\c_pi_fp/2}
+ }
+ }
+ }{
+ \fp_compare:nTF{#3>=\c_zero_fp}{ %II
+ \fp_compare:nNnTF\l_mix_asinarg_fp<\l_mix_halfsqrttwo_fp{
+ \fp_set:Nn#1{\c_pi_fp-\l_mix_arcsin_fp}
+ }{
+ \fp_set:Nn#1{\c_pi_fp/2-\l_mix_arccos_fp}
+ }
+ }{ %III
+ \fp_compare:nNnTF\l_mix_asinarg_fp<\l_mix_halfsqrttwo_fp{
+ \fp_set:Nn#1{-\c_pi_fp-\l_mix_arcsin_fp}
+ }{
+ \fp_set:Nn#1{\l_mix_arccos_fp-\c_pi_fp/2}
+ }
+ }
+ }
+ \fp_set:Nn#1{#1/deg}
+}
+
+\bool_new:N\l_mix_blankndname_bool
+
+%keys for parsing the 3D views file
+\keys_define:nn{media9/views}{
+ \par .code:n = {}, %allow for but ignore empty lines
+
+ VIEW .code:n = {
+ \int_compare:nF{\l_mix_level_tl<\c_zero_int}{
+ \msg_error:nnxx{media9}{nested~view}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}
+ }
+ \tl_set:Nn\l_mix_level_tl{\c_zero_int}
+ \tl_set:Nn\l_mix_cscount_tl{\c_zero_int}
+ \tl_set:Nx\l_mix_xname_tl{#1}%optional name of the view
+ \tl_trim_spaces:N\l_mix_xname_tl
+ %reset 3D settings to defaults
+ \mix_iiidreset:
+ },
+ VIEW .default:n = {},
+
+ COO .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{COO}{a~VIEW}
+ }
+ \tl_set:Nx\l_mix_coo_tl{#1}
+ },
+ COO .value_required:n = {true},
+
+ C2C .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{C2C}{a~VIEW}
+ }
+ \tl_set:Nx\l_mix_ctoc_tl{#1}
+ },
+ C2C .value_required:n = {true},
+
+ C2W .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{C2W}{a~VIEW}
+ }
+ \tl_set:Nx\l_mix_ciiw_tl{#1}
+ \bool_set_true:N\l_mix_ciiw_bool
+ },
+ C2W .value_required:n = {true},
+
+ ROO .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{ROO}{a~VIEW}
+ }
+ \fp_set:Nn\l_mix_roo_fp{abs(#1)}
+ \fp_compare:nNnT\l_mix_roo_fp<{1e-9}{
+ \fp_set:Nn\l_mix_roo_fp{1e-9}}
+ \bool_set_true:N\l_mix_roo_bool
+ },
+ ROO .value_required:n = {true},
+
+ AAC .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{AAC}{a~VIEW}
+ }
+ \fp_set:Nn\l_mix_aac_fp{#1}
+ },
+ AAC .value_required:n = {true},
+
+ ORTHO .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{ORTHO}{a~VIEW}
+ }
+ \bool_set_true:N\l_mix_iiidortho_bool
+ \fp_compare:nF{#1=0.0}{
+ \fp_set:Nn\l_mix_oscale_fp{#1}
+ \bool_if:NF\l_mix_roo_bool{
+ \fp_set:Nn\l_mix_temp_fp{1/#1/2}
+ \fp_set_eq:NN\l_mix_roo_fp\l_mix_temp_fp
+ }
+ }
+ },
+ ORTHO .default:n = {1},
+
+ PSOB .code:n = {
+ \int_compare:nTF{\l_mix_level_tl=\c_zero_int}{
+ \tl_set:Nx\l_mix_psob_tl{#1}
+ }{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}
+ {PSOB}{a~VIEW}
+ }
+ },
+ PSOB .value_required:n = {true},
+
+ ROLL .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{ROLL}{a~VIEW}
+ }
+ \fp_set:Nn\l_mix_roll_fp{#1}
+ },
+ ROLL .value_required:n = {true},
+
+ BGCOLOR .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{BGCOLOR}{a~VIEW}
+ }
+ \tl_set:Nx\l_mix_bg_tl{/BG<</CS/DeviceRGB/C[#1]/EA~true>>}
+ },
+ BGCOLOR .value_required:n = {true},
+
+ LIGHTS .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{LIGHTS}{a~VIEW}
+ }
+ \tl_set:Nx\l_mix_ls_tl{/LS<</Subtype/#1>>}
+ },
+ LIGHTS .value_required:n = {true},
+
+ RENDERMODE .code:n = {
+ \bool_if:nF{
+ \int_compare_p:n{\l_mix_level_tl=\c_zero_int} ||
+ \int_compare_p:n{\l_mix_level_tl=\c_one_int}
+ }{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}
+ {RENDERMODE}{a~VIEW~or~a~PART}
+ }
+ \int_compare:nT{\l_mix_level_tl=\c_zero_int}{
+ \tl_set:Nx\l_mix_rm_tl{/RM<</Subtype/#1>>}
+ }
+ \int_compare:nT{\l_mix_level_tl=\c_one_int}{
+ \tl_set:Nx\l_mix_ndrm_tl{/RM<</Subtype/#1>>} %part (=node) render mode
+ }
+ },
+ RENDERMODE .value_required:n = {true},
+
+ PARTSATTRS .choices:nn = {keep,restore}{
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{PARTSATTRS}{VIEW}
+ }
+ \tl_set:Nn\l_mix_keep_tl{keep}
+ \tl_if_eq:NNTF\l_keys_choice_tl\l_mix_keep_tl{
+ \tl_set:Nn\l_mix_nr_tl{/NR~false}
+ }{
+ \tl_set:Nn\l_mix_nr_tl{/NR~true}
+ }
+ },
+ PARTSATTRS .value_required:n = {true},
+
+ PART .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{section~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{PART}{VIEW}
+ }
+ \bool_set_true:N\l_mix_blankndname_bool
+ \bool_set:Nn\l_mix_blankndname_bool{\tl_if_blank_p:o{#1}}
+ \tl_set:Nn\l_mix_level_tl{\c_one_int}
+ \tl_set:Nn\l_mix_ndname_tl{(#1)} %part (=node) name
+ \tl_set_eq:NN\l_mix_partlineno_int\l_mix_lineno_int
+ %default part settings
+ \tl_clear:N\l_mix_ndop_tl %opacity
+ \tl_clear:N\l_mix_ndvi_tl %visibility
+ \tl_clear:N\l_mix_ndrm_tl %render mode
+ \tl_clear:N\l_mix_ndtrans_tl%transformation matrix
+ },
+ PART .default:n = {},
+
+ UTF16NAME .code:n = { %part (=node) name, UTF16, HEX encoded
+ \int_compare:nF{\l_mix_level_tl=\c_one_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{UTF16NAME}{PART}
+ }
+ \tl_if_blank:oT{#1}{
+ \msg_error:nnxx{media9}{missing~utf16~name}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}
+ }
+ \bool_set_false:N\l_mix_blankndname_bool
+ \mix_ndname:n{feff#1}
+ },
+ UTFNAME .default:n = {},
+
+ OPACITY .code:n = {
+ \bool_if:nF{
+ \int_compare_p:n{\l_mix_level_tl=\c_one_int} ||
+ \int_compare_p:n{\l_mix_level_tl=2}
+ }{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}
+ {OPACITY}{PART~or~CROSSSECT}
+ }
+ \int_compare:nT{\l_mix_level_tl=\c_one_int}{ %part
+ \tl_set:Nx\l_mix_ndop_tl{/O~#1}
+ }
+ \int_compare:nT{\l_mix_level_tl=2}{ %crosssect
+ \tl_set:Nx\l_mix_csop_tl{/PO~#1}
+ }
+ },
+ OPACITY .value_required:n = {true},
+
+ VISIBLE .choices:nn = {true,false}{
+ \bool_if:nF{
+ \int_compare_p:n{\l_mix_level_tl=\c_one_int} ||
+ \int_compare_p:n{\l_mix_level_tl=2}
+ }{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}
+ {VISIBLE}{PART~or~CROSSSECT}
+ }
+ \int_compare:nT{\l_mix_level_tl=\c_one_int}{ %part
+ \tl_set:Nx\l_mix_ndvi_tl{/V~\l_keys_choice_tl}
+ }
+ \int_compare:nT{\l_mix_level_tl=2}{ %crosssect
+ \tl_set:Nx\l_mix_cspv_tl{/PV~\l_keys_choice_tl}
+ }
+ },
+ VISIBLE .default:n = {true},
+
+ INTERSECTIONVISIBLE .choices:nn = {true,false}{
+ \int_compare:nF{\l_mix_level_tl=2}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{INTERSECTIONVISIBLE}
+ {CROSSSECT}
+ }
+ \tl_set:Nx\l_mix_csiv_tl{/IV~\l_keys_choice_tl}
+ },
+ INTERSECTIONVISIBLE .default:n = {true},
+
+ PLANECOLOR .code:n = {
+ \int_compare:nF{\l_mix_level_tl=2}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{PLANECOLOR}{CROSSSECT}
+ }
+ \tl_set:Nx\l_mix_cspc_tl{/PC~[/DeviceRGB~#1]}
+ },
+ PLANECOLOR .value_required:n = {true},
+
+ INTERSECTIONCOLOR .code:n = {
+ \int_compare:nF{\l_mix_level_tl=2}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{INTERSECTIONCOLOR}
+ {CROSSSECT}
+ }
+ \tl_set:Nx\l_mix_csic_tl{/IC~[/DeviceRGB~#1]}
+ },
+ INTERSECTIONCOLOR .value_required:n = {true},
+
+ TRANSFORM .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_one_int}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{TRANSLATE}{PART}
+ }
+ \seq_set_split:Nnn\l_mix_args_seq{~}{#1}
+ \seq_map_inline:Nn\l_mix_args_seq{
+ \fp_set:Nn\l_mix_tmpa_fp{##1}
+ \tl_set:Nx\l_mix_tmpa_tl{\fp_to_decimal:n{round((\l_mix_tmpa_fp),9)}}
+ \tl_put_right:Nx\l_mix_ndtrans_tl{~\l_mix_tmpa_tl}
+ }
+ },
+ TRANSFORM .value_required:n = {true},
+
+ CROSSSECT .code:n = {
+ \int_compare:nF{\l_mix_level_tl=\c_zero_int}{
+ \msg_error:nnxxxx{media9}{section~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{CROSSSECT}{VIEW}
+ }
+ \int_compare:nT{\l_mix_cscount_tl>\c_zero_int}{
+ \msg_warning:nnxx{media9}{too~many~cross~sections}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}
+ }
+ \tl_set:Nn\l_mix_level_tl{2}
+ %default settings
+ \tl_set:Nn\l_mix_cscentre_tl{0~0~0} %centre of clipping plane
+ \tl_set:Nn\l_mix_csorient_tl{null~0~0} %orientation of plane
+ \tl_clear:N\l_mix_csop_tl %opacity
+ \tl_clear:N\l_mix_cspv_tl %plane visibility
+ \tl_clear:N\l_mix_cspc_tl %plane colour
+ \tl_clear:N\l_mix_csic_tl %intersection colour
+ \tl_clear:N\l_mix_csiv_tl %intersection visibility
+ },
+ CROSSSECT .value_forbidden:n = {true},
+
+ CENTER .code:n = {
+ \int_compare:nF{\l_mix_level_tl=2}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{CENTER}{CROSSSECT}
+ }
+ \tl_set:Nx\l_mix_args_tl{#1\space}
+ \exp_after:wN\mix_parse_vect:w\l_mix_args_tl\q_stop
+ \tl_set:Nx\l_mix_x_tl{\fp_to_decimal:n{round((\l_mix_x_fp),9)}}
+ \tl_set:Nx\l_mix_y_tl{\fp_to_decimal:n{round((\l_mix_y_fp),9)}}
+ \tl_set:Nx\l_mix_z_tl{\fp_to_decimal:n{round((\l_mix_z_fp),9)}}
+ \tl_set:Nx\l_mix_cscentre_tl{
+ \l_mix_x_tl\space
+ \l_mix_y_tl\space
+ \l_mix_z_tl
+ }
+ },
+ CENTER .value_required:n = {true},
+
+ NORMAL .code:n = {
+ \int_compare:nF{\l_mix_level_tl=2}{
+ \msg_error:nnxxxx{media9}{key~not~allowed}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}{NORMAL}{CROSSSECT}
+ }
+ \tl_set:Nx\l_mix_args_tl{#1\space}
+ \exp_after:wN\mix_parse_vect:w\l_mix_args_tl\q_stop
+ \fp_set:Nn\l_mix_magxy{ %sqrt(x^2+y^2)
+ (\l_mix_x_fp*\l_mix_x_fp + \l_mix_y_fp*\l_mix_y_fp)**0.5}
+ %rot angle of normal vec around Y axis
+ \mix_angfromxy:Nnn\l_mix_roty_fp\l_mix_magxy\l_mix_z_fp
+ \fp_set:Nn\l_mix_roty_fp{-\l_mix_roty_fp}
+ %rot angle of normal vec around Z axis
+ \mix_angfromxy:Nnn\l_mix_rotz_fp\l_mix_x_fp\l_mix_y_fp
+ \tl_set:Nx\l_mix_roty_tl{\fp_to_decimal:n{round((\l_mix_roty_fp),9)}}
+ \tl_set:Nx\l_mix_rotz_tl{\fp_to_decimal:n{round((\l_mix_rotz_fp),9)}}
+ \tl_set:Nx\l_mix_csorient_tl{null~\l_mix_roty_tl\space\l_mix_rotz_tl}
+ },
+ NORMAL .value_required:n = {true},
+
+ END .code:n = {
+ \int_case:nnF{\l_mix_level_tl}{
+ {0}{ %VIEW
+ \bool_if:NF\l_mix_ciiw_bool{
+ \tl_set:Nx\l_mix_args_tl{\l_mix_coo_tl\space\l_mix_ctoc_tl\space}
+ \exp_after:wN\mix_calc_ciiw:w\l_mix_args_tl\q_stop
+ }
+ \int_incr:N\l_mix_viewcnt_int
+ \tl_if_blank:VT\l_mix_xname_tl{
+ \tl_set:Nx\l_mix_xname_tl{View~\int_use:N\l_mix_viewcnt_int}
+ }
+ \mix_view:n{\l_mix_xname_tl}
+ \tl_put_right:Nx\l_mix_iiidviewarray_tl{~\mix_pdflastobj:}
+ \tl_set:Nn\l_mix_level_tl{-1}
+ }
+
+ {1}{ %PART
+ \bool_if:NT\l_mix_blankndname_bool{
+ \msg_error:nnxx{media9}{missing~part~name}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_partlineno_int}
+ }
+
+ \mix_pdfobj:nnn{}{dict}{
+ /Type/3DNode/N~\l_mix_ndname_tl
+ \l_mix_ndop_tl\l_mix_ndvi_tl\l_mix_ndrm_tl
+ \tl_if_blank:VF\l_mix_ndtrans_tl{
+ /M~[\l_mix_ndtrans_tl]
+ }
+ }
+
+ \tl_put_right:Nx\l_mix_naarray_tl{~\mix_pdflastobj:}
+ \tl_set:Nn\l_mix_level_tl{\c_zero_int}
+ }
+ {2}{ %CROSSSECT
+ \int_compare:nT{\l_mix_cscount_tl=\c_zero_int}{
+ \mix_pdfobj:nnn{}{dict}{
+ /Type/3DCrossSection
+ /C~[\l_mix_cscentre_tl]
+ /O~[\l_mix_csorient_tl]
+ \l_mix_csop_tl\l_mix_cspv_tl\l_mix_cspc_tl
+ \l_mix_csic_tl\l_mix_csiv_tl
+ }
+ \tl_put_right:Nx\l_mix_saarray_tl{~\mix_pdflastobj:}
+ \tl_set:Nn\l_mix_cscount_tl{\c_one_int}
+ }
+ \tl_set:Nn\l_mix_level_tl{\c_zero_int}
+ }
+ }{
+ \msg_error:nnxx{media9}{no~end~here}
+ {\g_mix_vfile_tl}{\int_use:N\l_mix_lineno_int}
+ }
+ },
+ END .value_forbidden:n = {true},
+}
+
+%options for \mediabutton command
+
+%helpers for option parsing
+\cs_new:Npn\mix_separate_cmd_from_actn:w#1[#2]#3\q_stop{#1}
+\cs_new:Npn\mix_separate_arg_from_actn:w#1[#2]#3\q_stop{#2}
+\prg_generate_conditional_variant:Nnn\regex_match:nn{nV}{TF}
+\prg_generate_conditional_variant:Nnn\regex_match:nn{nV}{T}
+\prg_generate_conditional_variant:Nnn\tl_if_blank:n{v}{F}
+\prg_generate_conditional_variant:Nnn\tl_if_eq:nn{Vn}{TF}
+
+\bool_new:N\g_mix_btndraft_bool
+\keys_define:nn{media9/mbtndraft}{
+ draft .choice:,
+ draft / true .code:n = {\bool_gset_true:N\g_mix_btndraft_bool},
+ draft / false .code:n = {\bool_gset_false:N\g_mix_btndraft_bool},
+ draft .default:n = {true},
+
+ final .choice:,
+ final / true .code:n = {\bool_gset_false:N\g_mix_btndraft_bool},
+ final / false .code:n = {\bool_gset_true:N\g_mix_btndraft_bool},
+ final .default:n = {true},
+
+ unknown .code:n = {}
+}
+\keys_define:nn{media9/mbtn}{
+ draft .code:n = {},
+ final .code:n = {},
+
+ tooltip .tl_gset_x:N = \g_mix_tooltip_tl,
+ tooltip .value_required:n = {true},
+
+ overface .code:n = {
+ \sbox\l_mix_poster_box{
+ \bool_if:NTF\g_pbs_lscape_bool{\rotatebox{90}{#1}}{#1}}
+ \mix_pdfxform:n{\l_mix_poster_box}
+ \tl_gset:Nx\g_mix_overbtn_tl{\mix_pdflastxform:}
+ },
+ overface .value_required:n = {true},
+
+ downface .code:n = {
+ \sbox\l_mix_poster_box{
+ \bool_if:NTF\g_pbs_lscape_bool{\rotatebox{90}{#1}}{#1}}
+ \mix_pdfxform:n{\l_mix_poster_box}
+ \tl_gset:Nx\g_mix_downbtn_tl{\mix_pdflastxform:}
+ },
+ downface .value_required:n = {true},
+
+ 3Dgotoview .code:n = {
+ \tl_set:Nx\l_tmpa_tl{#1} %extract ref
+ \regex_replace_once:nnN{([^:]+):??.*}{\1}\l_tmpa_tl
+ \tl_trim_spaces:N\l_tmpa_tl
+ \tl_if_exist:cTF{ann@\l_tmpa_tl}{
+ \str_if_eq:eeF{\tl_use:c{ann@\l_tmpa_tl}}{draft}{
+ \tl_set:Nx\l_tmpb_tl{#1} %extract idx
+ \regex_replace_once:nnN{[^:]+:?}{}\l_tmpb_tl
+ \tl_trim_spaces:N\l_tmpb_tl
+ \tl_if_blank:VF\l_tmpb_tl{
+ \regex_match:nVTF{^[DFLNP]$}\l_tmpb_tl{
+ \tl_set:Nx\l_mix_vidx_tl{/\l_tmpb_tl}
+ }{
+ \regex_match:nVTF{^\d+$}\l_tmpb_tl{
+ \tl_set:Nx\l_mix_vidx_tl{~\l_tmpb_tl}
+ }{
+ \regex_replace_once:nnN{^\(\s*}{}\l_tmpb_tl
+ \regex_replace_once:nnN{\s*\)$}{}\l_tmpb_tl
+ \tl_set:Nx\l_mix_vidx_tl{~(\l_tmpb_tl)}
+ }
+ }
+ }
+ \mix_pdfobj:nnn{}{dict}{
+ /S/JavaScript/JS~(
+ if(typeof(annotRM)=='undefined'){annotRM=new~Array();}
+ if(typeof(annotRM['\l_tmpa_tl'])=='undefined'){
+ annotRM['\l_tmpa_tl']=
+ this.getAnnotRichMedia(\tl_use:c{page@\l_tmpa_tl},'\l_tmpa_tl');
+ }
+ annotRM['\l_tmpa_tl'].activated=true;
+ )
+ \tl_if_blank:VF\l_tmpb_tl{
+ /Next~<<
+ /S/GoTo3DView
+ /TA~\tl_use:c{ann@\l_tmpa_tl}
+ /V\l_mix_vidx_tl
+ >>
+ }
+ }
+ \tl_gput_right:Nx\g_mix_btnactions_tl{~\mix_pdflastobj:}
+ }
+ }{
+ \msg_warning:nnx{media9}{undefined~reference}{\l_tmpa_tl}
+ \cs_if_exist:NF\g_mix_refundefwarned_tl{
+ \tl_new:N\g_mix_refundefwarned_tl
+ \AtEndDocument{\msg_warning:nn{media9}{undefined~references}}
+ }
+ }
+ },
+ 3Dgotoview .value_required:n = {true},
+
+ mediacommand .code:n = {
+ \tl_set:Nx\l_tmpa_tl{#1}
+ \regex_replace_once:nnN{([^:]+):??.*}{\1}\l_tmpa_tl %extract ref
+ \tl_trim_spaces:N\l_tmpa_tl
+ \tl_if_exist:cTF{ann@\l_tmpa_tl}{
+ \str_if_eq:eeF{\tl_use:c{ann@\l_tmpa_tl}}{draft}{
+ \tl_set:Nx\l_tmpb_tl{#1}
+ \regex_replace_once:nnN{[^:]+\:?}{}\l_tmpb_tl
+ \tl_trim_spaces:N\l_tmpb_tl
+ \regex_replace_all:nnN{^\{|\}$}{}\l_tmpb_tl
+ \tl_set:Nx\l_mix_cmd_tl{
+ \exp_after:wN\mix_separate_cmd_from_actn:w\l_tmpb_tl[]\q_stop}
+ \tl_trim_spaces:N\l_mix_cmd_tl
+ \tl_set:Nx\l_mix_arg_tl{
+ \exp_after:wN\mix_separate_arg_from_actn:w\l_tmpb_tl[]\q_stop}
+ \tl_trim_spaces:N\l_mix_arg_tl
+ \mix_pdfobj:nnn{}{dict}{
+ /S/JavaScript/JS~(
+ if(typeof(annotRM)=='undefined'){annotRM=new~Array();}
+ if(typeof(annotRM['\l_tmpa_tl'])=='undefined'){
+ annotRM['\l_tmpa_tl']=
+ this.getAnnotRichMedia(\tl_use:c{page@\l_tmpa_tl},'\l_tmpa_tl');
+ }
+ annotRM['\l_tmpa_tl'].activated=true;
+ )
+ \tl_if_blank:VF\l_mix_cmd_tl{
+ /Next~<<
+ /S/RichMediaExecute
+ /TA~\tl_use:c{ann@\l_tmpa_tl}
+ /TI~\tl_use:c{main@\l_tmpa_tl}
+ /CMD~<<
+ /C~(\l_mix_cmd_tl)
+ \tl_if_blank:VF\l_mix_arg_tl{
+ /A~[\l_mix_arg_tl]
+ }
+ >>
+ >>
+ }
+ }
+ \tl_gput_right:Nx\g_mix_btnactions_tl{~\mix_pdflastobj:}
+ }
+ }{
+ \msg_warning:nnx{media9}{undefined~reference}{\l_tmpa_tl}
+ \cs_if_exist:NF\g_mix_refundefwarned_tl{
+ \tl_new:N\g_mix_refundefwarned_tl
+ \AtEndDocument{\msg_warning:nn{media9}{undefined~references}}
+ }
+ }
+ },
+ mediacommand .value_required:n = {true},
+
+ jsaction .code:n = {
+ \tl_set:Nx\l_tmpa_tl{#1}
+ \regex_match:nVTF{^[^\{:]+:.*$}\l_tmpa_tl{
+ \regex_replace_once:nnN{([^:]+):??.*}{\1}\l_tmpa_tl %extract ref
+ \tl_trim_spaces:N\l_tmpa_tl
+ }{\tl_clear:N\l_tmpa_tl}
+ \tl_set:Nx\l_tmpb_tl{#1}
+ \tl_if_blank:VF\l_tmpa_tl{
+ \tl_if_exist:cF{ann@\l_tmpa_tl}{
+ \msg_warning:nnx{media9}{undefined~reference}{\l_tmpa_tl}
+ \cs_if_exist:NF\g_mix_refundefwarned_tl{
+ \tl_new:N\g_mix_refundefwarned_tl
+ \AtEndDocument{\msg_warning:nn{media9}{undefined~references}}
+ }
+ }
+ \regex_replace_once:nnN{[^:]+\:}{}\l_tmpb_tl
+ }
+ \tl_set:Nx\l_tmpb_tl{\exp_after:wN\use:n\l_tmpb_tl}
+ \tl_trim_spaces:N\l_tmpb_tl
+ \tl_if_blank:VF\l_tmpb_tl{
+ \mix_pdfobj:nnn{}{dict}{
+ /S/JavaScript/JS~(
+ try{\l_tmpb_tl}catch(e){console.show();console.println(e)})}}
+ \bool_lazy_and:nnT{
+ \tl_if_exist_p:c{ann@\l_tmpa_tl}
+ }{
+ !\str_if_eq_p:ee{\tl_use:c{ann@\l_tmpa_tl}}{draft}
+ }{
+ \mix_pdfobj:nnn{}{dict}{
+ /S/JavaScript/JS~(
+ if(typeof(annotRM)=='undefined'){annotRM=new~Array();}
+ if(typeof(annotRM['\l_tmpa_tl'])=='undefined'){
+ annotRM['\l_tmpa_tl']=
+ this.getAnnotRichMedia(\tl_use:c{page@\l_tmpa_tl},'\l_tmpa_tl');
+ }
+ annotRM['\l_tmpa_tl'].activated=true;
+ )
+ \tl_if_blank:VF\l_tmpb_tl{/Next~\mix_pdflastobj:}
+ }
+ }
+ \bool_if:nT{
+ \bool_lazy_and_p:nn{
+ \tl_if_exist_p:c{ann@\l_tmpa_tl}
+ }{
+ !\str_if_eq_p:ee{\tl_use:c{ann@\l_tmpa_tl}}{draft}
+ }
+ || !\tl_if_blank_p:V\l_tmpb_tl
+ }{
+ \tl_gput_right:Nx\g_mix_btnactions_tl{~\mix_pdflastobj:}
+ }
+ },
+ jsaction .value_required:n = {true},
+
+ unknown .code:n = {
+ \msg_error:nnx{media9}{unknown~option}{\l_keys_key_tl}
+ }
+}
+
+%create media buttons
+\int_new:N\g_mix_mbtncnt_int
+\NewDocumentCommand\mediabutton{O{}m}{%#1 options, #2 normal button text
+ \mix_uriend:
+ \group_begin:
+ \mix_beginLTR:
+ \leavevmode
+ %reset various variables
+ \tl_gclear:N\g_mix_overbtn_tl
+ \tl_gclear:N\g_mix_downbtn_tl
+ \tl_gclear:N\g_mix_btnactions_tl
+ \tl_gclear:N\g_mix_tooltip_tl
+ %process options
+ \bool_gset_eq:NN\g_mix_btndraft_bool\g_mix_pkgdraft_bool
+ \mix_uribegin: %treat URI characters correctly
+ \keys_set:nn{media9/mbtndraft}{#1} %detect draft/final on first pass
+ \bool_if:NF\g_mix_btndraft_bool{\keys_set:nn{media9/mbtn}{#1}}
+ \mix_uriend:
+ %normal button appearance
+ %\hbox_set:Nn\l_mix_poster_box{#2}
+ \sbox\l_mix_poster_box{#2}
+ \tl_set:Nx\width {\dim_use:N\box_wd:N\l_mix_poster_box}
+ \tl_set:Nx\height{\dim_use:N\box_ht:N\l_mix_poster_box}
+ \tl_set:Nx\depth {\dim_use:N\box_dp:N\l_mix_poster_box}
+ \bool_if:NTF\g_mix_btndraft_bool{\box_use:N\l_mix_poster_box}{
+ \sbox\l_mix_poster_box{\bool_if:NTF\g_pbs_lscape_bool{\rotatebox{90}{
+ \box_use:N\l_mix_poster_box}}{\box_use:N\l_mix_poster_box}}
+ \mix_pdfxform:n{\l_mix_poster_box}
+ \tl_set:Nx\g_mix_normalbtn_tl{\mix_pdflastxform:}
+ %insert widget annotation
+ \mix_pdfannot:nnnn{\width}{\height}{\depth}{
+ /Subtype/Widget
+ /T~(mbtn@\int_use:N\g_mix_mbtncnt_int)
+ \tl_if_empty:NF\g_mix_tooltip_tl{/TU~(\g_mix_tooltip_tl)}
+ /FT/Btn/Ff~65536
+ \tl_if_empty:NTF\g_mix_downbtn_tl{/H/I}{/H/P}
+ /AP~<<
+ /N~\g_mix_normalbtn_tl
+ \tl_if_empty:NF\g_mix_overbtn_tl{/R~\g_mix_overbtn_tl}
+ \tl_if_empty:NF\g_mix_downbtn_tl{/D~\g_mix_downbtn_tl}
+ >>
+ /MK~<<
+ /TP~1~/IF~<</S/A/FB~true/A~[0.0~0.0]>>
+ /I~\g_mix_normalbtn_tl
+ \tl_if_empty:NF\g_mix_overbtn_tl{/RI~\g_mix_overbtn_tl}
+ \tl_if_empty:NF\g_mix_downbtn_tl{/IX~\g_mix_downbtn_tl}
+ >>
+ /A~<<
+ /S/JavaScript/JS~(app.focusRect=false;)
+ %sequence of actions to perform
+ \tl_if_empty:NF\g_mix_btnactions_tl{/Next~[\g_mix_btnactions_tl]}
+ >>
+ }
+ \mix_appendtofields:n{\mix_pdflastann:}
+ \hbox_to_wd:nn{\width}{
+ \vrule~width~\c_zero_dim~height~\height~depth~\depth\hss
+ }
+ \int_gincr:N\g_mix_mbtncnt_int
+ }
+ \mix_endLTR:
+ \group_end:
+}
+\tl_set_eq:NN\l_mix_mediabutton_tl\mediabutton
+\tl_set:Nn\mediabutton{\mix_uribegin:\l_mix_mediabutton_tl}
diff --git a/macros/latex/contrib/media9/pdfbase.sty b/macros/latex/contrib/media9/pdfbase.sty
new file mode 100644
index 0000000000..37cbd103ff
--- /dev/null
+++ b/macros/latex/contrib/media9/pdfbase.sty
@@ -0,0 +1,1412 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% pdfbase.sty
+%
+% driver independent access to low-level pdf features
+%
+% Copyright 2015--\today, Alexander Grahn
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This package implements commands for the creation of PDF Objects,
+% Form XObjects, Image XObjects, annotations, links, marked content (BDC/EMC)
+% and for manipulating the PDF catalog.
+%
+% Supported workflows:
+%
+% pdflatex, lualatex
+% latex-->dvips-->ps2pdf or Distiller
+% latex-->dvipdfmx
+% xelatex
+%
+% Package options:
+%
+% xetex
+% dvipdfmx
+% dvisvgm (basic support: \pbs_literal:nn, \pbs_pdfannot:nnnn,
+% \pbs_pdfxform:nnnnn\pbs_pdfrefxform:n)
+% bigfiles (for embedding large files as stream objects; only relevant for
+% dvips mode, ignored otherwise)
+%
+% Commands defined:
+%
+% \pbs_pdfobj:nnn
+% #1: predefined PDF object ID to be used for the current obj; may be empty
+% #2: type of object ( generic | dict | array | stream | fstream )
+% #3: if #2==
+% `generic', then single basic object, such as 3.141, (foo), true, /Name
+% `dict', then PDF key-value dictionary
+% `stream', then
+% {stream attributes as PDF key-value dictionary}{content string}
+% `fstream', then
+% {stream attributes as PDF key-value dictionary}{file name}
+%
+% if #3 && #1 are both empty, an object reference will be reserved for later
+% use as #1
+%
+% \pbs_pdflastobj:
+% inserts object ID of PDF object created/processed during most recent call
+% of \pbs_pdfobj:nnn
+%
+% --------
+%
+% \pbs_pdfannot:nnnn
+% #1: width, #2: height, #3: depth, #4: dictionary (key-value)
+%
+% \pbs_pdflastann:
+% inserts object ID of PDF object created during most recent call of
+% \pbs_pdfannot:nnnn
+%
+% --------
+%
+% \pbs_appendtofields:n
+% #1 object ID of PDF annotation; annotations of /Subtype/Widget
+% should be appended to the /Fields array of the global /AcroForm dictionary
+%
+% --------
+%
+% \pbs_pdflink:nn
+% #1: dictionary (key-value), #2: text
+%
+% --------
+%
+% \pbs_pdfdest:nnnn
+% #1: name, #2: fit | fitb | fitbh | fith | fitbv | fitv | xyz | fitr
+% #3: zoom, #4: text
+%
+% --------
+%
+% \pbs_pdfxform:nnnnn
+% #1: add pgf/tikz resources (transparency, shading)? (1|0) %dvipdfmx/xetex
+% #2: used as PDF annotation appearance? (1|0) %dvips/pdftex
+% #3: additional resources %all BUT dvips
+% #4: additional dictionary entries
+% #5: savebox number
+% creates PDF Form XObject from savebox content
+%
+% \pbs_pdflastxform:
+% inserts object ID of PDF Form XObject created during most recent call of
+% \pbs_pdfxform:nnnnn
+%
+% \pbs_pdfrefxform:n
+% #1: xform object ID
+% inserts the PDF Form XObject into the current content stream, that is,
+% typsets the PDF Form XObject
+%
+% --------
+%
+% \pbs_pdfximage:n
+% #1: bitmap image file name
+% creates PDF Image XObject from /bitmap/ file for use as bitmap resource
+% in 3D context
+%
+% \pbs_pdflastximage:
+% inserts object ID of PDF Image XObject created during most recent call of
+% \pbs_pdfximage:n
+%
+% --------
+%
+% \pbs_literal:nn
+% #1: keyword (empty) | direct | page
+% #2: raw PDF/Postscript code
+% implements \pdfliteral{...}, \pdfliteral direct {...},
+% \pdfliteral page {...} from pdfTeX, and
+% \special{" ...} and \special{ps: ...} from dvips
+%
+% --------
+%
+% \pbs_pdfcatalog:n
+% #1: dictionary (key-value)
+%
+% --------
+%
+% marked content BDC/EMC operators
+% \pbs_pdfbdc:nn ...
+% ... \pbs_pdfemc:
+% #1: tag, #2: properties dictionary obj ID
+%
+% --------
+%
+% alternative \AtEndDvi
+% \pbs_at_end_dvi:n
+% #1: \specials or \write to be inserted on the very last page
+%
+% --------
+%
+% \pbs_add_form_font: (pdfLaTeX, LuaLaTeX)
+% adds current font as a resource to the global /AcroForm dict, allowing
+% the font to be used in PDF Forms (theoretically, see
+% https://acrobat.uservoice.com/forums/590923-acrobat-for-windows-and-mac/
+% suggestions/33077827-bug-in-text-field-forms-embedded-opentype-font )
+%
+% \pbs_last_form_font: (pdfLaTeX, LuaLaTeX)
+% expands to current font's resource name; to be used in the /DA (...)
+% entry of the Form dictionary
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License.
+%
+% The latest version of this license is in
+%
+% http://mirrors.ctan.org/macros/latex/base/lppl.txt
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is A. Grahn.
+
+%process non-detectable driver options for passing to expl3
+\RequirePackage{xkeyval}
+\DeclareOptionX{dvipdfmx}{%
+ \PassOptionsToPackage{driver=dvipdfmx}{expl3}
+}
+\DeclareOptionX{dvisvgm}{%
+ \PassOptionsToPackage{driver=dvisvgm}{expl3}
+}
+\DeclareOptionX*{}\ProcessOptionsX*\relax %allow anything as an option
+
+\RequirePackage{expl3}
+\RequirePackage{l3keys2e}
+
+\def\g@pbs@date@tl{2019/07/11}
+\def\g@pbs@version@tl{0.33}
+
+\ProvidesExplPackage{pdfbase}{\g@pbs@date@tl}{\g@pbs@version@tl}
+{driver independent access to low-level PDF features}
+
+\msg_set:nnnn{pdfbase}{support~outdated}{
+ Support~package~`#1'~too~old.
+}{
+ Get~an~up~to~date~version~of~`#1'.\\
+ Aborting.
+}
+\@ifpackagelater{expl3}{2019/05/09}{}{
+ \msg_error:nnn{pdfbase}{support~outdated}{expl3.sty}
+ \tex_endinput:D
+}
+
+\int_new:N\g_pbs_page_int %abs. page counter
+
+%creating global definitions
+\cs_new:Npn\pbs@newkey#1#2{\tl_gset:cx{#1}{#2}}
+
+\msg_set:nnn{pdfbase}{rerun}{Rerun~to~get~internal~references~right!}
+
+%LaTeX2e wrapper
+\cs_new_nopar:Npn\pbs@seq@push@cx#1#2{
+ \seq_if_exist:cF{#1}{\seq_new:c{#1}}
+ \seq_gput_right:cx{#1}{#2}
+}
+
+%wrong image file type for Image XObject generation
+\msg_gset:nnn{pdfbase}{wrong~image~resource}{
+ Image~resource~file\\~~'#1'\\has~wrong~type.\\\\
+ Driver~#2~only~accepts~files~of~type\\#3\\
+ as~image~resources.
+}
+
+%pdfTeX version check
+\sys_if_engine_pdftex:T{
+ \int_compare:nT{\pdftexversion<130}{
+ \msg_interrupt:nnn{pdfbase~error}{pdfTeX,~version~>=~1.30~required.}{}
+ }
+}
+
+% page (bop, eop) hooks
+\cs_new_protected:Nn\pbs_bop_action:n{\seq_gput_right:Nn\g_pbs_bop_seq{#1}}
+\cs_new_protected:Nn\pbs_eop_action:n{\seq_gput_right:Nn\g_pbs_eop_seq{#1}}
+\seq_new:N\g_pbs_bop_seq
+\seq_new:N\g_pbs_eop_seq
+
+% own AtEndDvi implementation
+\cs_new_protected:Nn\pbs_at_end_dvi:n{\seq_gput_right:Nn\g_pbs_eod_seq{#1}}
+\seq_new:N\g_pbs_eod_seq
+\pbs@newkey{pbs@last@page}{0}
+
+% AtEndDvi check
+\AtBeginDocument{
+ \iow_now:Nx\@mainaux{
+ \token_to_str:N\providecommand\token_to_str:N\pbs@newkey[2]{}
+ }
+ \iow_now:Nx\@mainaux{
+ \token_to_str:N\providecommand\token_to_str:N\pbs@seq@push@cx[2]{}
+ }
+ \iow_now:Nx\@mainaux{
+ \token_to_str:N\providecommand\token_to_str:N\pbs@at@end@dvi@check{}
+ }
+ \iow_now:Nx\@mainaux{\token_to_str:N\pbs@at@end@dvi@check}
+ \cs_new_protected:Npn\pbs@at@end@dvi@check{
+ \bool_if:nT{
+ !\tl_if_exist:NTF\pbs@last@page{
+ \int_compare_p:n{\pbs@last@page=\g_pbs_page_int}
+ }{
+ \c_false_bool
+ }
+ }{
+ \cs_if_exist:NF\g_pbs_rerunwarned_tl{
+ \tl_new:N\g_pbs_rerunwarned_tl
+ \msg_warning:nn{pdfbase}{rerun}
+ }
+ }
+ }
+}
+
+%%%%%%%%%%%%%%%%%
+%package options
+%%%%%%%%%%%%%%%%%
+%unknown package option error message
+\msg_gset:nnnn{pdfbase}{unknown~package~option}{Unknown~package~option~`#1'.}{
+ Package option~'#1'~is~unknown;\\
+ perhaps~it~is~spelled~incorrectly.
+}
+
+\bool_new:N\g_pbs_pkgbigfiles_bool
+\bool_new:N\g_pbs_dvipdfmx_bool
+\bool_new:N\g_pbs_dvisvgm_bool
+\bool_new:N\g_pbs_lscape_bool %if we are inside landscape env
+\AtBeginDocument{
+ \cs_if_exist:NT\landscape{%
+ \tl_put_right:Nn\landscape{\bool_gset_true:N\g_pbs_lscape_bool}
+ \tl_put_left:Nn\endlandscape{\bool_gset_false:N\g_pbs_lscape_bool}
+ }
+}
+
+\keys_define:nn{pdfbase}{
+ xetex .code:n = {}, %dummy, we use engine test instead
+
+ dvipdfmx .choice:,
+ dvipdfmx / true .code:n = {
+ \bool_gset_true:N\g_pbs_dvipdfmx_bool
+ \bool_gset_false:N\g_pbs_dvisvgm_bool
+ },
+ dvipdfmx / false .code:n = {\bool_gset_false:N\g_pbs_dvipdfmx_bool},
+ dvipdfmx .default:n = {true},
+
+ dvisvgm .choice:,
+ dvisvgm / true .code:n = {
+ \bool_gset_true:N\g_pbs_dvisvgm_bool
+ \bool_gset_false:N\g_pbs_dvipdfmx_bool
+ },
+ dvisvgm / false .code:n = {\bool_gset_false:N\g_pbs_dvisvgm_bool},
+ dvisvgm .default:n = {true},
+
+ bigfiles .bool_gset:N = \g_pbs_pkgbigfiles_bool,
+
+ unknown .code:n = {
+ \msg_error:nnx{pdfbase}{unknown~package~option}{\l_keys_key_tl}
+ }
+}
+\ProcessKeysOptions{pdfbase}
+
+\sys_if_output_pdf:T{
+ \bool_gset_false:N\g_pbs_dvipdfmx_bool
+ \bool_gset_false:N\g_pbs_dvisvgm_bool
+}
+
+\sys_if_engine_xetex:T{
+ \bool_if:NF\g_pbs_dvisvgm_bool{\bool_gset_true:N\g_pbs_dvipdfmx_bool}
+}
+
+\cs_new_nopar:Nn\pbs_insert_properties_entry:{}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%commands for creating PDF objects, annots etc.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\sys_if_output_pdf:TF{
+ %in LuaTeX-0.95.0, pdfTeX primitives got new names
+ \bool_lazy_and:nnT{
+ \sys_if_engine_luatex_p:
+ }{
+ !\int_compare_p:n{\luatexversion<95}
+ }{
+ \cs_set_protected:Npn\pdfnames{\pdfextension~names~}
+ \cs_set_protected:Npn\pdfobj{\pdfextension~obj~}
+ \cs_set_protected:Npn\pdfrefobj{\pdfextension~refobj~}
+ \cs_set_protected:Npn\pdfannot{\pdfextension~annot~}
+ \cs_set_protected:Npn\pdfstartlink{\pdfextension~startlink~}
+ \cs_set_protected:Npn\pdfendlink{\pdfextension~endlink\relax}
+ \cs_set_protected:Npn\pdfdest{\pdfextension~dest~}
+ \cs_set_protected:Npn\pdfliteral{\pdfextension~literal~}
+ \cs_set_protected:Npn\pdfcatalog{\pdfextension~catalog~}
+ \cs_set:Npn\pdflastlink{\numexpr\pdffeedback~lastlink\relax}
+ \cs_set:Npn\pdflastobj{\numexpr\pdffeedback~lastobj\relax}
+ \cs_set:Npn\pdflastannot{\numexpr\pdffeedback~lastannot\relax}
+ \cs_set:Npn\pdfpageref{\numexpr\pdffeedback~pageref\relax}
+ \cs_set:Npx\pdfpageresources{\pdfvariable~pageresources}
+ \cs_set_eq:NN\pdfximage\saveimageresource
+ \cs_set_eq:NN\pdfrefximage\useimageresource
+ \cs_set_eq:NN\pdflastximage\lastsavedimageresourceindex
+ \cs_set_eq:NN\pdflastximagepages\lastsavedimageresourcepages
+ \cs_set_eq:NN\pdfxform\saveboxresource
+ \cs_set_eq:NN\pdfrefxform\useboxresource
+ \cs_set_eq:NN\pdflastxform\lastsavedboxresourceindex
+ \cs_set:Npn\pdffontobjnum{\pdffeedback~fontobjnum}
+ }
+
+ %helper func to remove `0 R' part from pdf obj reference
+ \cs_new_nopar:Nn\pbs_reftonum:n{\_pbs_reftonum:f{#1}}
+ \cs_new_nopar:Nn\_pbs_reftonum:n{\exp_after:wN\_pbs_reftonum:w#1}
+ \cs_generate_variant:Nn\_pbs_reftonum:n{f}
+ \cs_new_nopar:Npn\_pbs_reftonum:w #1~0~R{#1}
+
+ %literal PDF code into content stream, no saving of graphics state
+ \cs_new_protected_nopar:Nn\pbs_literal:nn{ % #1: empty (`'), `direct' or `page'
+ \str_case:nnF{#1}{ % #2: raw PDF
+ % `direct' inserts raw pdf code without translating origin (0,0) to
+ % current position: origin is lower left page corner
+ {direct}{\pdfliteral~direct~{#2}}
+ % same as above, but closing text object if necessary
+ {page}{\pdfliteral~page~{#2}}
+ }{
+ % closing text object if necessary and setting current
+ % location's coordinates to (0,0)
+ \pdfliteral{#2}
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfobj:nnn{
+ \tl_clear:N\l_pbs_usenum_tl
+ \tl_if_blank:oF{#1}{
+ \tl_set:Nx\l_pbs_usenum_tl{useobjnum~\pbs_reftonum:n{#1}}
+ }
+ \bool_if:nTF{\tl_if_blank_p:o{#1}&&\tl_if_blank_p:o{#3}}{
+ \pdfobj~reserveobjnum
+ }{
+ \str_case:nn{#2}{
+ {generic}{\immediate\pdfobj~\l_pbs_usenum_tl~{#3}}
+ {dict}{\immediate\pdfobj~\l_pbs_usenum_tl~{<<#3>>}}
+ {array}{\immediate\pdfobj~\l_pbs_usenum_tl~{[#3]}}
+ {stream}{\immediate\pdfobj~\l_pbs_usenum_tl~stream~
+ attr{\use_i:nn#3}~{\use_ii:nn#3}
+ }
+ {fstream}{\immediate\pdfobj~\l_pbs_usenum_tl~stream~
+ attr{\use_i:nn#3}~file~{\use_ii:nn#3}
+ }
+ }
+ }
+ \tl_gset:Nx\g_pbs_pdflastobj_tl{\the\pdflastobj\space 0~R}
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfannot:nnnn{
+ \immediate\pdfannot~width~#1~height~#2~depth~#3 {
+ \cs_if_exist_use:N\ocgbase_insert_oc:~#4}
+ \tl_gset:Nx\g_pbs_pdflastann_tl{\the\pdflastannot\space 0~R}
+ }
+
+ \cs_new_protected:Nn\pbs_pdflink:nn{
+ \leavevmode
+ \immediate\pdfstartlink~user~{
+ \cs_if_exist_use:N\ocgbase_insert_oc:~#1}#2\pdfendlink
+ }
+
+ \cs_new_protected:Nn\pbs_pdfdest:nnnn{
+ \leavevmode
+ \str_case:nnTF{#2}{
+ {fit}{}
+ {fitb}{}
+ {fitbv}{}
+ {fitv}{}
+ }{
+ \pdfdest~name~{#1}~#2~#4
+ }{
+ \group_begin:
+ \hbox_set:Nn\l_tmpa_box{#4}
+ \str_case:nnTF{#2}{
+ {fitbh}{\tl_set:Nn\l_pbs_fittype_tl{#2}}
+ {fith}{\tl_set:Nn\l_pbs_fittype_tl{#2}}
+ {xyz}{\tl_set:Nn\l_pbs_fittype_tl{#2~zoom~\int_eval:n{#3*1000}}}
+ }{
+ \box_move_up:nn{\box_ht:N\l_tmpa_box}{\hbox:n{
+ \pdfdest~name~{#1}~\l_pbs_fittype_tl
+ }}#4
+ }{
+ \pdfdest~name~{#1}~fitr~
+ width~\box_wd:N\l_tmpa_box~
+ height~\box_ht:N\l_tmpa_box~
+ depth~\box_dp:N\l_tmpa_box
+ \box_use:N\l_tmpa_box
+ }
+ \box_clear:N\l_tmpa_box
+ \group_end:
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfxform:nnnnn{ % #1 not used
+ %additional resources
+ \tl_set:Nx\l_tmpa_tl{\the\pdfpageresources~#3}\tl_trim_spaces:N\l_tmpa_tl
+ %additional dict entries
+ \tl_set:Nx\l_tmpb_tl{#4}
+ \tl_trim_spaces:N\l_tmpb_tl
+ \int_compare:nT{#2>\c_zero_int}{\immediate}
+ \pdfxform~
+ \str_if_eq:eeF{\l_tmpb_tl}{}{attr~{\l_tmpb_tl}~}
+ \str_if_eq:eeF{\l_tmpa_tl}{}{resources~{\l_tmpa_tl}~}#5
+ \tl_gset:Nx\g_pbs_pdflastxform_tl{\the\pdflastxform\space 0~R}
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfrefxform:n{% #1: xform obj ID
+ \hbox_overlap_right:n{\smash{\pdfrefxform\pbs_reftonum:n{#1}}}
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfximage:n{
+ \filename@parse{#1}
+ \tl_set:Nx\l_pbs_ext_tl{\tl_lower_case:n{\filename@ext}}
+ \bool_if:nTF{
+ \str_if_eq_p:Vn\l_pbs_ext_tl{png}
+ ||\str_if_eq_p:Vn\l_pbs_ext_tl{jpg}
+ ||\str_if_eq_p:Vn\l_pbs_ext_tl{jpeg}
+ ||\str_if_eq_p:Vn\l_pbs_ext_tl{jbig2}
+ ||\str_if_eq_p:Vn\l_pbs_ext_tl{jb2}
+ }{
+ \immediate\pdfximage{#1}
+ \tl_gset:Nx\g_pbs_pdflastximage_tl{\the\pdflastximage\space 0~R}
+ }{
+ \msg_error:nnnnn{pdfbase}{wrong~image~resource}{#1}{pdftex}{
+ png,~jpeg~and~jbig2
+ }
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfcatalog:n{\pdfcatalog{#1}}
+
+ \int_new:N\g_pbs_oc_int %object ID for marked content Properties
+
+ \cs_new_protected_nopar:Nn\pbs_zap_properties:{ %strip /Properties dict from
+ \group_begin: %/pdfpageresources
+ \tl_set:Nx\l_pbs_temp_tl{\group_end:
+ \global\pdfpageresources{
+ \exp_after:wN\_pbs_zap_properties:w\the\pdfpageresources/Properties<<>>.
+ }
+ }\l_pbs_temp_tl
+ }
+ \cs_new_nopar:Npn\_pbs_zap_properties:w#1/Properties<<#2>>#3{
+ \bool_if:nTF{\tl_if_empty_p:n{#2}&&\str_if_eq_p:nn{#3}{.}}{#1}{
+ #1\_pbs_zap_properties:w#3
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfbdc:nn{
+ \pdfliteral~page~{#1/rm@oc\int_use:N\g_pbs_oc_int\space BDC}
+ %decide whether the current property is to be written to the page
+ %resources or to the xobject resources, depending on whether marked content
+ %is written to a page stream or to an xobject stream (for compatibility with
+ %`xsavebox' package)
+ \bool_if:nTF{
+ \cs_if_exist:NTF\xsb_count_props:{
+ \int_compare_p:n{\xsb_count_props:>\c_zero_int}
+ }{
+ \c_false_bool
+ }
+ }{
+ \xsb_addto_props:n{/rm@oc\int_use:N\g_pbs_oc_int\space#2}
+ }{
+ \iow_shipout_x:Nx\@mainaux{\token_to_str:N\pbs@seq@push@cx{
+ pbs@props@\exp_not:N\int_use:N\g_pbs_page_int
+ }{/rm@oc\int_use:N\g_pbs_oc_int\space#2}}
+ }
+ \int_gincr:N\g_pbs_oc_int
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfemc:{\pdfliteral~page~{EMC}}
+
+ %inserts /Properties <<...>> entry into page resources
+ \cs_gset:Nn\pbs_insert_properties_entry:{
+ \pbs_zap_properties: %purge those from previous page
+ \tl_set:Nx\l_tmpa_tl{\seq_if_exist:cT{pbs@props@\int_use:N\g_pbs_page_int}{
+ \seq_use:cn{pbs@props@\int_use:N\g_pbs_page_int}{~}}}
+ \tl_trim_spaces:N\l_tmpa_tl
+ \str_if_eq:eeF{\l_tmpa_tl}{}{
+ \group_begin:
+ \tl_set:Nx\l_pbs_temp_tl{\group_end:
+ \global\pdfpageresources{
+ \the\pdfpageresources
+ /Properties<<\seq_if_exist:cT{pbs@props@\int_use:N\g_pbs_page_int}{
+ \seq_use:cn{pbs@props@\int_use:N\g_pbs_page_int}{~}
+ }>>
+ }
+ }\l_pbs_temp_tl
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_add_form_font:{
+ \cs_if_exist:cF{pbs_form_font_\pdffontobjnum\font}{
+ \tl_new:c{pbs_form_font_\pdffontobjnum\font}
+ \tl_gput_right:Nx\g_pbs_form_fonts_tl{
+ ~/FormFont\pdffontobjnum\font\space\pdffontobjnum\font\space 0~R
+ }
+ }
+ \tl_gset:Nx\g_pbs_last_form_font_tl{/FormFont\pdffontobjnum\font}
+ }
+ \cs_new_nopar:Nn\pbs_last_form_font:{\g_pbs_last_form_font_tl}
+}{
+ %pgf + transparency related settings
+ \bool_new:N\g_pbs_pgfloaded_bool
+ \bool_gset_false:N\g_pbs_pgfloaded_bool
+ \AtBeginDocument{
+ \@ifpackageloaded{pgf}{\bool_gset_true:N\g_pbs_pgfloaded_bool}{}
+ }
+ \int_new:N\g_pbs_obj_int %object ID
+ \bool_if:NTF\g_pbs_dvipdfmx_bool{ %dvipdfmx/XeTeX
+ \AtBeginDocument{
+ % suppress any annotation growth through (x)dvipdfmx option/config var `g'
+ \special{dvipdfmx:config~g~0}
+ % suppress link destination renaming (as in original dvipdfm
+ \special{dvipdfmx:config~C~0x10} % and in pre-2014 dvipdfmx)
+ }
+
+ %literal PDF code into content stream; open text objects are always closed
+ \cs_new_protected_nopar:Nn\pbs_literal:nn{ % #1: empty (`'), `direct' or `page'
+ \str_if_eq:nnTF{#1}{}{ % #2: raw PDF
+ % set current location's coordinates to (0,0), while saving graphics
+ % state before and re-instating after insertion (this is different from
+ % \pdfliteral{...}
+ \special{pdf:content~#2}
+ }{
+ % `direct' works the same as `page': no origin translation,
+ % no gs saving, but closing open text objects
+ \special{pdf:code~#2}
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfobj:nnn{
+ \tl_if_blank:oTF{#1}{
+ \tl_set:Nx\l_pbs_usenum_tl{@pbs@obj\int_use:N\g_pbs_obj_int}
+ \int_gincr:N\g_pbs_obj_int
+ }{
+ \tl_set:Nx\l_pbs_usenum_tl{#1}
+ }
+ \tl_if_blank:oF{#3}{
+ \str_case:nn{#2}{
+ {generic}{\special{pdf:obj~\l_pbs_usenum_tl\space #3}}
+ {dict}{\special{pdf:obj~\l_pbs_usenum_tl\space<<#3>>}}
+ {array}{\special{pdf:obj~\l_pbs_usenum_tl\space[#3]}}
+ {stream}{\special{pdf:stream~\l_pbs_usenum_tl\space
+ (\use_ii:nn#3)<<\use_i:nn#3>>
+ }}
+ {fstream}{
+ \message{<\use_ii:nn#3>}
+ \special{pdf:fstream~\l_pbs_usenum_tl\space
+ (\use_ii:nn#3)<<\use_i:nn#3>>
+ }
+ }
+ }
+ }
+ \tl_gset_eq:NN\g_pbs_pdflastobj_tl\l_pbs_usenum_tl
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfannot:nnnn{
+ \special{pdf:ann~@pbs@obj\int_use:N\g_pbs_obj_int\space
+ width~\dim_eval:n{#1}~height~\dim_eval:n{#2}~depth~\dim_eval:n{#3}~
+ <<\cs_if_exist_use:N\ocgbase_insert_oc:~#4>>
+ }
+ \tl_gset:Nx\g_pbs_pdflastann_tl{@pbs@obj\int_use:N\g_pbs_obj_int}
+ \int_gincr:N\g_pbs_obj_int
+ }
+
+ \cs_new_protected:Nn\pbs_pdflink:nn{
+ \leavevmode
+ \special{pdf:bann~<<\cs_if_exist_use:N\ocgbase_insert_oc:~#1>>}#2
+ \special{pdf:eann}
+ }
+
+ \cs_new_protected:Nn\pbs_pdfdest:nnnn{
+ \leavevmode
+ \str_case:nnTF{#2}{
+ {fit}{\tl_set:Nn\l_pbs_fittype_tl{/Fit}}
+ {fitb}{\tl_set:Nn\l_pbs_fittype_tl{/FitB}}
+ {fitbv}{\tl_set:Nn\l_pbs_fittype_tl{/FitBV~@xpos}}
+ {fitv}{\tl_set:Nn\l_pbs_fittype_tl{/FitV~@xpos}}
+ }{
+ \special{pdf:~dest~(#1)~[~@thispage~\l_pbs_fittype_tl]}#4
+ }{
+ \group_begin:
+ \hbox_set:Nn\l_tmpa_box{#4}
+ \str_case:nnTF{#2}{
+ {fitbh}{\tl_set:Nn\l_pbs_fittype_tl{/FitBH~@ypos}}
+ {fith}{\tl_set:Nn\l_pbs_fittype_tl{/FitH~@ypos}}
+ {xyz}{\tl_set:Nn\l_pbs_fittype_tl{/XYZ~@xpos~@ypos~#3}}
+ }{
+ \box_move_up:nn{\box_ht:N\l_tmpa_box}{\hbox:n{
+ \special{pdf:~dest~(#1)~[~@thispage~\l_pbs_fittype_tl]}
+ }}#4
+ }{ % FitR
+ \box_move_down:nn{\box_dp:N\l_tmpa_box}{\hbox:n{
+ \pbs_pdfobj:nnn{}{generic}{@xpos}
+ \tl_gset_eq:NN\g_pbs_llx_tl\g_pbs_pdflastobj_tl
+ \pbs_pdfobj:nnn{}{generic}{@ypos}
+ \tl_gset_eq:NN\g_pbs_lly_tl\g_pbs_pdflastobj_tl
+ }}
+ \box_use:N\l_tmpa_box
+ \box_move_up:nn{\box_ht:N\l_tmpa_box}{\hbox:n{
+ \special{pdf:~dest~(#1)~[@thispage~
+ /FitR~
+ \g_pbs_llx_tl\space\g_pbs_lly_tl\space @xpos~@ypos
+ ]}
+ }}
+ }
+ \box_clear:N\l_tmpa_box
+ \group_end:
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfxform:nnnnn{ % #2 not used
+ \begin{picture}(0,0)
+ \put(0,0){
+ \special{pdf:bxobj~@pbs@obj\int_use:N\g_pbs_obj_int\space
+ width~\dim_eval:n{\box_wd:N#5}~
+ height~\space\dim_eval:n{\box_ht:N#5}~
+ depth~\space \dim_eval:n{\box_dp:N#5}
+ }
+ \box_use:N#5
+ \tl_clear:N\l_tmpa_tl{}
+ %transparency et al. for PGF
+ \bool_if:nT{\int_compare_p:n{#1>\c_zero_int} && \g_pbs_pgfloaded_bool}{
+ \ifpgf@sys@pdf@extgs@exists
+ \tl_set:Nn\l_tmpa_tl{/ExtGState~@pgfextgs}
+ \fi
+ \ifpgf@sys@pdf@patterns@exists
+ \tl_put_right:Nn\l_tmpa_tl{/Pattern~@pgfpatterns}
+ \fi
+ \ifpgf@sys@pdf@colorspaces@exists
+ \tl_put_right:Nn\l_tmpa_tl{/ColorSpace~@pgfcolorspaces}
+ \fi
+ }
+ %additional resources
+ \tl_put_right:Nx\l_tmpa_tl{~#3}\tl_trim_spaces:N\l_tmpa_tl
+ \str_if_eq:eeF{\l_tmpa_tl}{}{
+ \special{pdf:put~@resources~<<\l_tmpa_tl>>}
+ }
+ %additional dict entries
+ \tl_set:Nx\l_tmpa_tl{#4}
+ \tl_trim_spaces:N\l_tmpa_tl
+ \special{pdf:exobj %close form xobject
+ \str_if_eq:eeF{\l_tmpa_tl}{}{<<\l_tmpa_tl>>}
+ }
+ }
+ \end{picture}
+ \tl_gset:Nx\g_pbs_pdflastxform_tl{@pbs@obj\int_use:N\g_pbs_obj_int}
+ \int_gincr:N\g_pbs_obj_int
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfrefxform:n{\special{pdf:uxobj~#1}}% #1: xform obj ID
+
+ \cs_new_protected_nopar:Nn\pbs_pdfximage:n{
+ \filename@parse{#1}
+ \tl_set:Nx\l_pbs_ext_tl{\tl_lower_case:n{\filename@ext}}
+ \bool_if:nTF{
+ \str_if_eq_p:Vn\l_pbs_ext_tl{png}
+ ||\str_if_eq_p:Vn\l_pbs_ext_tl{jpg}
+ ||\str_if_eq_p:Vn\l_pbs_ext_tl{jpeg}
+ }{
+ \special{pdf:image~@pbs@obj\int_use:N\g_pbs_obj_int\space
+ %move initial display off the page
+ matrix~0.000001~0~0~0.000001~-1000000~-1000000~(#1)}
+ \tl_gset:Nx\g_pbs_pdflastximage_tl{@pbs@obj\int_use:N\g_pbs_obj_int}
+ \int_gincr:N\g_pbs_obj_int
+ }{
+ \msg_error:nnnnn{pdfbase}{wrong~image~resource}{#1}{dvipdfmx/xetex}{
+ png~and~jpeg
+ }
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfcatalog:n{\special{pdf:put~@catalog~<<#1>>}}
+
+ \int_new:N\g_pbs_oc_int %object ID
+ \cs_new_protected_nopar:Nn\pbs_pdfbdc:nn{
+ \special{pdf:code~#1/rm@oc\int_use:N\g_pbs_oc_int\space BDC}
+ \special{pdf:put~@resources~<<
+ /Properties~<</rm@oc\int_use:N\g_pbs_oc_int\space#2>>>>}
+ \int_gincr:N\g_pbs_oc_int
+ }
+ \cs_new_nopar:Nn\pbs_pdfemc:{\special{pdf:code~EMC}}
+ }{
+ \bool_if:NTF\g_pbs_dvisvgm_bool{
+ \tl_gset:Nx\g_pbs_hash_tl{\token_to_str:N#}
+ %insert literal Postscript code
+ \cs_new_protected_nopar:Nn\pbs_literal:nn{ % #1: empty (`'), `direct' or `page'
+ \str_if_eq:nnTF{#1}{}{ % #2: raw Postscript
+ % set current location's coordinates to (0,0) and set unit vectors to
+ % 1bp right and 1bp upwards; graphics state is saved before and
+ % re-instated after insertion
+ \special{"~#2}
+ }{
+ % `direct' does the same as `page': no origin translation,
+ % no gs saving
+ \special{ps::~#2}
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfannot:nnnn{
+ \special{dvisvgm:raw~{?nl}<path~d=}
+ \group_begin:\dim_set:Nn\unitlength{1pt}
+ \begin{picture}(0,0)% mark annotation rectangle
+ \put(0,-\dim_to_decimal:n{#3}){% ll
+ \special{dvisvgm:raw~'M{?x}~{?y}}
+ }
+ \put(\dim_to_decimal:n{#1},-\dim_to_decimal:n{#3}){% lr
+ \special{dvisvgm:raw~{?nl}L{?x}~{?y}}
+ }
+ \put(\dim_to_decimal:n{#1},\dim_to_decimal:n{#2}){% ur
+ \special{dvisvgm:raw~{?nl}L{?x}~{?y}}
+ }
+ \put(0,\dim_to_decimal:n{#2}){% ul
+ \special{dvisvgm:raw~{?nl}L{?x}~{?y}~Z'}
+ }
+ \end{picture}
+ \group_end:
+ \special{dvisvgm:raw~{?nl}opacity='0'~#4~class='annot'/>}
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfxform:nnnnn{
+ \makebox[\c_zero_dim][l]{
+ \raisebox{\c_zero_dim}[\c_zero_dim][\c_zero_dim]{
+ \special{dvisvgm:raw~{?nl}<defs>{?nl}
+ <
+ g~transform='translate(-{?x},-{?y})'~#4~
+ id='pbs@obj\int_use:N\g_pbs_obj_int'~class='xform'
+ >
+ }
+ \box_use_drop:N#5
+ \special{dvisvgm:raw~{?nl}</g>{?nl}</defs>}
+ }
+ }
+ \tl_gset:Nx\g_pbs_pdflastxform_tl{\g_pbs_hash_tl pbs@obj\int_use:N\g_pbs_obj_int}
+ \int_gincr:N\g_pbs_obj_int
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfrefxform:n{
+ \special{dvisvgm:raw~{?nl}<use~x='{?x}'~y='{?y}'~xlink:href='#1'/>}
+ }
+ }{
+ %dvips
+ \bool_new:N\g_pbs_powerdot_bool %to cope with some powerdot oddity
+ \@ifclassloaded{powerdot}{\bool_gset_true:N\g_pbs_powerdot_bool}{}
+
+ \sys_if_engine_pdftex:TF{
+ \cs_new_nopar:Nn\pbs_filesize:n{\pdffilesize{#1}}
+ \cs_new_nopar:Nn\pbs_filedump:nnn{\pdffiledump~offset~#1~length~#2~{#3}}
+ }{
+ \sys_if_engine_luatex:T{
+ \RequirePackage{pdftexcmds}
+ \cs_new_nopar:Nn\pbs_filesize:n{\pdf@filesize{#1}}
+ \cs_new_nopar:Nn\pbs_filedump:nnn{\pdf@filedump{#1}{#2}{#3}}
+ }
+ }
+
+ \AtBeginDocument{
+ \@ifpackageloaded{hyperref}{}{
+ %define `?pdfmark' operator as in file pdfmark.def from package `hyperref'
+ \special{!~
+ systemdict~/pdfmark~known
+ {
+ userdict~/?pdfmark~systemdict~/exec~get~put
+ }{
+ userdict~/?pdfmark~systemdict~/pop~get~put~
+ userdict~/pdfmark~systemdict~/cleartomark~get~put
+ }
+ ifelse
+ }
+ }
+ \special{!~
+ %keeping track of xform nesting level
+ true~setglobal~globaldict~/pbs@xform@level~0~put~false~setglobal
+ %back-transforms user coords to page coords (bigpoints with reference
+ %point [0,0] in the bottom-left page corner)
+ % user_x user_y pbs@user2page --> page_x page_y
+ /pbs@user2page~{
+ 0~begin~% make everything local in here
+ /y~exch~def~/x~exch~def~
+ matrix~currentmatrix~
+ matrix~defaultmatrix~
+ matrix~invertmatrix~
+ matrix~concatmatrix~cvx~exec~
+ /ty~exch~def~/tx~exch~def~
+ /d~exch~def~/c~exch~def~
+ /b~exch~def~/a~exch~def~
+ x~a~mul~y~c~mul~add~tx~add~
+ x~b~mul~y~d~mul~add~ty~add~
+ end
+ }~def~
+ /pbs@user2page~load~0~1~dict~put % insert dict at index 0;
+ } % dict is allocated only once
+ }
+ \cs_new:Nn\pbs_special:n{\special{ps:~SDict~begin~#1~end}}
+
+ \bool_if:NT\g_pbs_pkgbigfiles_bool{
+ \special{psfile=\jobname.pbsdat}
+ %open auxiliary file \jobname.pbsdat for writing hex encoded streams of
+ %the files to be embedded. This file is inserted into PS during dvips.
+ \iow_new:N\g_pbs_mstreams_stream
+ \iow_open:Nn\g_pbs_mstreams_stream{\jobname.pbsdat}
+ \iow_now:Nn\g_pbs_mstreams_stream{
+ /M9D~1~dict~def~M9D~begin
+ /o{mark/_objdef}bind~def/O{/type/stream/OBJ~pdfmark}bind~def
+ /m~systemdict/mark~get~def
+ /P{/ASCIIHexDecode~filter/PUT~pdfmark}bind~def
+ /PP{/PUT~pdfmark}bind~def
+ /C{/CLOSE~pdfmark}bind~def~end
+ }
+ }
+
+ %insert literal Postscript code
+ \cs_new_protected_nopar:Nn\pbs_literal:nn{ % #1: empty (`'), `direct' or `page'
+ \str_if_eq:nnTF{#1}{}{ % #2: raw Postscript
+ % set current location's coordinates to (0,0) and set unit vectors to
+ % 1bp right and 1bp upwards; graphics state is saved before and
+ % re-instated after insertion
+ \special{"~#2}
+ }{
+ % `direct' does the same as `page': no origin translation,
+ % no gs saving
+ \special{ps::~#2}
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfobj:nnn{
+ \tl_clear:N\l_pbs_usenum_tl
+ \tl_if_blank:oTF{#1}{
+ \tl_set:Nx\l_pbs_usenum_tl{{pbs@obj\int_use:N\g_pbs_obj_int}}
+ \int_gincr:N\g_pbs_obj_int
+ }{
+ \tl_set:Nx\l_pbs_usenum_tl{#1}
+ }
+ \str_if_eq:nnT{#2}{generic}{
+ \msg_interrupt:nnn{pdfbase~error}{
+ generic~object~creation~not~supported~by~PDFmarks
+ }{}
+ }
+ \tl_if_blank:oF{#3}{
+ \bool_if:nTF{
+ \g_pbs_pkgbigfiles_bool &&
+ \str_if_eq_p:nn{#2}{fstream}
+ }{
+ \iow_now:Nx\g_pbs_mstreams_stream{
+ M9D~begin~o\l_pbs_usenum_tl O
+ }
+ }{
+ \pbs_special:n{mark~/_objdef~\l_pbs_usenum_tl\space/type
+ \str_case:nn{#2}{
+ {generic}{}
+ {dict}{/dict}
+ {array}{/array}
+ {stream}{/stream}
+ {fstream}{/stream}
+ }~
+ /OBJ~pdfmark
+ }
+ }
+ \str_case:nn{#2}{
+ {generic}{}
+ {dict}{\pbs_special:n{mark~\l_pbs_usenum_tl~<<#3>>/PUT~pdfmark}}
+ {array}{
+ \pbs_special:n{mark~\l_pbs_usenum_tl~0~[#3]/PUTINTERVAL~pdfmark}
+ }
+ {stream}{\special{ps::[nobreak]~SDict~begin~
+ mark~\l_pbs_usenum_tl~(\use_ii:nn#3)/PUT~pdfmark~
+ mark~\l_pbs_usenum_tl~<<\use_i:nn#3>>/PUT~pdfmark~end
+ }}
+ {fstream}{
+ \tl_set:Nn\l_pbs_offset_tl{0}
+ \tl_set:Nx\l_pbs_fsize_tl{\pbs_filesize:n{\use_ii:nn#3}}
+ \message{<\use_ii:nn#3}
+ %embed file in chunks of 32768 Bytes into PS as chunks of
+ %65536 Bytes of HEX code
+ \bool_while_do:nn{
+ \int_compare_p:n{\l_pbs_offset_tl<\l_pbs_fsize_tl}
+ }{
+ \bool_if:NTF\g_pbs_pkgbigfiles_bool{
+ \iow_now:Nx\g_pbs_mstreams_stream{
+ m\l_pbs_usenum_tl
+ (\pbs_filedump:nnn{\l_pbs_offset_tl}{32767}{
+ \use_ii:nn#3
+ })P
+ }
+ }{
+ \pbs_special:n{
+ mark~
+ \l_pbs_usenum_tl~
+ (\pbs_filedump:nnn{\l_pbs_offset_tl}{32767}{
+ \use_ii:nn#3
+ })~
+ /ASCIIHexDecode~filter~/PUT~
+ pdfmark
+ }
+ }
+ \tl_set:Nx\l_pbs_offset_tl{\int_eval:n{\l_pbs_offset_tl+32767}}
+ \message{.}
+ }
+ \bool_if:NTF\g_pbs_pkgbigfiles_bool{
+ \iow_now:Nx\g_pbs_mstreams_stream{
+ m\l_pbs_usenum_tl<<\use_i:nn#3>>PP~
+ m\l_pbs_usenum_tl~C~end
+ }
+ }{
+ \pbs_special:n{
+ mark~\l_pbs_usenum_tl~<<\use_i:nn#3>>~/PUT~pdfmark~
+ mark~\l_pbs_usenum_tl~/CLOSE~pdfmark
+ }
+ }
+ \message{>}
+ }
+ }
+ }
+ \tl_gset_eq:NN\g_pbs_pdflastobj_tl\l_pbs_usenum_tl
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfannot:nnnn{
+ \group_begin:
+ \dim_set:Nn\unitlength{1pt}
+ \begin{picture}(0,0)% mark annotation rectangle
+ \put(0,-\dim_to_decimal:n{#3}){
+ \pbs_special:n{currentpoint~/pbs@lly~exch~def~/pbs@llx~exch~def}
+ }
+ \put(\dim_to_decimal:n{#1},\dim_to_decimal:n{#2}){
+ \pbs_special:n{currentpoint~/pbs@ury~exch~def~/pbs@urx~exch~def}
+ }
+ \end{picture}
+ \group_end:
+ \str_if_eq:eeF{#4}{}{
+ \pbs_special:n{
+ mark~
+ /_objdef~{pbs@obj\int_use:N\g_pbs_obj_int}
+ /Rect~[pbs@llx~pbs@lly~pbs@urx~pbs@ury]
+ \cs_if_exist_use:N\ocgbase_insert_oc:~#4
+ /ANN~pdfmark
+ }
+ \tl_gset:Nx\g_pbs_pdflastann_tl{{pbs@obj\int_use:N\g_pbs_obj_int}}
+ \int_gincr:N\g_pbs_obj_int
+ }
+ }
+
+ \cs_new_protected:Nn\pbs_pdflink:nn{
+ \leavevmode
+ \cs_if_exist:NTF\pdfmark{
+ \pdfmark[#2]{pdfmark=/ANN,Raw={
+ \cs_if_exist_use:N\ocgbase_insert_oc:~#1}}
+ }{
+ \hbox_set:Nn\l_tmpb_box{#2}
+ \pbs_pdfannot:nnnn{
+ \dim_use:N\box_wd:N\l_tmpb_box}{
+ \dim_use:N\box_ht:N\l_tmpb_box}{
+ \dim_use:N\box_dp:N\l_tmpb_box
+ }{#1}
+ \box_use_drop:N\l_tmpb_box
+ }
+ }
+
+ \cs_new_protected:Nn\pbs_pdfdest:nnnn{
+ \leavevmode
+ \group_begin:
+ %write destination page number to aux
+ \iow_shipout_x:Nx\@mainaux{
+ \token_to_str:N\pbs@newkey{pbs@#1@destpage}{
+ \exp_not:N\int_use:N\exp_not:N\g_pbs_page_int}
+ }
+ \cs_if_exist:cF{pbs@#1@destpage}{
+ \tl_set:cn{pbs@#1@destpage}{0}
+ \cs_if_exist:NF\g_pbs_rerunwarned_tl{
+ \tl_new:N\g_pbs_rerunwarned_tl
+ \msg_warning:nn{pdfbase}{rerun}
+ }
+ }
+ \str_case:nnTF{#2}{
+ {fit}{\tl_set:Nn\l_pbs_fittype_tl{/Fit}}
+ {fitb}{\tl_set:Nn\l_pbs_fittype_tl{/FitB}}
+ }{
+ \pbs_special:n{
+ mark~/Dest~(#1)~cvn~/Page~\tl_use:c{pbs@#1@destpage}~/View~[
+ \l_pbs_fittype_tl
+ ]~/DEST~pdfmark
+ }
+ #4
+ }{
+ \hbox_set:Nn\l_tmpa_box{#4}
+ %mark anchor/view rect, insert text, insert destination
+ \str_case:nnTF{#2}{
+ {fitbh}{\tl_set:Nn\l_pbs_fittype_tl{/FitBH}}
+ {fith}{\tl_set:Nn\l_pbs_fittype_tl{/FitH}}
+ }{
+ \box_move_up:nn{\box_ht:N\l_tmpa_box}{\hbox:n{
+ \pbs_special:n{
+ currentpoint~pbs@user2page~/pbs@top~exch~def~pop~
+ mark~/Dest~(#1)~cvn~/Page~\tl_use:c{pbs@#1@destpage}~/View~[
+ \l_pbs_fittype_tl\space pbs@top
+ ]~/DEST~pdfmark
+ }
+ }}
+ #4
+ }{
+ \str_case:nnTF{#2}{
+ {fitbv}{\tl_set:Nn\l_pbs_fittype_tl{/FitBV}}
+ {fitv}{\tl_set:Nn\l_pbs_fittype_tl{/FitV}}
+ }{
+ \pbs_special:n{
+ currentpoint~pbs@user2page~pop~/pbs@left~exch~def~
+ mark~/Dest~(#1)~cvn~/Page~\tl_use:c{pbs@#1@destpage}~/View~[
+ \l_pbs_fittype_tl\space pbs@left
+ ]~/DEST~pdfmark
+ }
+ #4
+ }{
+ \str_case:nn{#2}{
+ {xyz}{
+ \box_move_up:nn{\box_ht:N\l_tmpa_box}{\hbox:n{
+ \pbs_special:n{
+ currentpoint~pbs@user2page~
+ /pbs@top~exch~def~/pbs@left~exch~def~
+ mark~/Dest~(#1)~cvn~/Page~\tl_use:c{pbs@#1@destpage}~
+ /View~[
+ /XYZ~pbs@left~pbs@top~#3
+ ]~/DEST~pdfmark
+ }
+ }}
+ #4
+ }
+ {fitr}{
+ \box_move_down:nn{\box_dp:N\l_tmpa_box}{\hbox:n{
+ \pbs_special:n{
+ currentpoint~pbs@user2page~
+ /pbs@lly~exch~def~/pbs@llx~exch~def
+ }
+ }}
+ \box_use:N\l_tmpa_box
+ \box_move_up:nn{\box_ht:N\l_tmpa_box}{\hbox:n{
+ \pbs_special:n{
+ currentpoint~pbs@user2page~
+ /pbs@ury~exch~def~/pbs@urx~exch~def~
+ mark~/Dest~(#1)~cvn~/Page~\tl_use:c{pbs@#1@destpage}~
+ /View~[
+ /FitR~pbs@llx~pbs@lly~pbs@urx~pbs@ury
+ ]~/DEST~pdfmark
+ }
+ }}
+ }
+ }
+ }
+ }
+ \box_clear:N\l_tmpa_box
+ }
+ \group_end:
+ }
+
+ \msg_set:nnn{pdfbase}{content~too~large}{
+ Line~\msg_line_number: :\\
+ Content~exceeds~paper~size~(width~and/or~height)\\
+ of~the~document~and~may~be~clipped~in~the~final\\
+ output.
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfxform:nnnnn{ % #1, #3 not used (resources are
+ %this allows nesting \pbs_pdfxform:nnnnn{...} % managed by pdfmarks)
+ \pbs_special:n{
+ gsave~
+ pbs@xform@level~0~eq~{/pbs@ctm@0~matrix~currentmatrix~def}~if~
+ pbs@ctm@0~setmatrix~
+ true~setglobal~
+ globaldict~/pbs@xform@level~pbs@xform@level~1~add~put~
+ false~setglobal
+ }
+ %rescale box to fit within the papersize while distilling
+ \tl_gset:cx{scale_{pbs@obj\int_use:N\g_pbs_obj_int}}{\fp_eval:n{min(1.0,
+ \dim_ratio:nn{\paperwidth}{\box_wd:N#5},
+ \dim_ratio:nn{\paperheight}{\box_ht:N#5+\box_dp:N#5}
+ )}}
+ \box_scale:Nnn#5{
+ \tl_use:c{scale_{pbs@obj\int_use:N\g_pbs_obj_int}}
+ }{
+ \tl_use:c{scale_{pbs@obj\int_use:N\g_pbs_obj_int}}
+ }
+ \group_begin:
+ %mark bbox of box#5
+ \dim_set:Nn\unitlength{1pt}
+ \begin{picture}(0,0)
+ \put(0,-\dim_to_decimal:n{\box_dp:N#5}){
+ \pbs_special:n{
+ currentpoint~/pbs@lly~exch~def~/pbs@llx~exch~def
+ }
+ }
+ \put(
+ \dim_to_decimal:n{\box_wd:N#5},
+ \dim_to_decimal:n{\box_ht:N#5}
+ ){
+ \pbs_special:n{
+ currentpoint~/pbs@ury~exch~def~/pbs@urx~exch~def
+ }
+ }
+ \end{picture}
+ \group_end:
+ %additional dict entries
+ \tl_set:Nx\l_tmpa_tl{#4} \tl_trim_spaces:N\l_tmpa_tl
+ \pbs_special:n{
+ gsave~
+ currentpoint~/pbs@cury~exch~def~/pbs@curx~exch~def~
+ %translate graphics to upper left page corner
+ \bool_if:nF{\g_pbs_powerdot_bool||\g_pbs_lscape_bool}{
+ {
+ pbs@llx~neg~pbs@ury~neg~translate~
+ Resolution~neg~VResolution~neg~translate %one inch left, one inch up
+ }?pdfmark~
+ }
+ %distill graphics to XObject
+ gsave~pbs@curx~pbs@cury~translate~ %move origin (0,0) to the current
+ mark~ %position
+ /_objdef~{pbs@obj\int_use:N\g_pbs_obj_int}
+ /BBox~[
+ pbs@llx~pbs@curx~sub~pbs@lly~pbs@cury~sub~
+ pbs@urx~pbs@curx~sub~pbs@ury~pbs@cury~sub
+ ]
+ %insert additional dict entries (the Distiller way)
+ \str_if_eq:eeF{\l_tmpa_tl}{}{
+ product~(Distiller)~search~{pop~pop~pop~\l_tmpa_tl}{pop}ifelse
+ }
+ /BP~pdfmark~grestore~
+ \int_compare:nT{#2>\c_zero_int}{
+ %operations needed if used as annotation appearance
+ {
+ isls{%landscape mode (powerdot, geometry /w landscape option)
+ /pbs@dxdy~{
+ pbs@urx~pbs@llx~sub~abs~
+ pbs@ury~pbs@lly~sub~abs~div
+ }~bind~def
+ /pbs@dydx~{1~pbs@dxdy~div}~bind~def
+ /pbs@cx~{pbs@urx~pbs@llx~add~2~div}~bind~def
+ /pbs@cy~{pbs@ury~pbs@lly~add~2~div}~bind~def
+ %graphics needs to be rescaled for some reason ...
+ [pbs@dxdy~0~0~pbs@dydx~1~pbs@dxdy~sub~pbs@cx~mul~
+ 1~pbs@dydx~sub~pbs@cy~mul]~concat
+ %... rotated by 270 degrees ...
+ [0~-1~1~0~pbs@cx~pbs@cy~sub~pbs@cx~pbs@cy~add]~concat
+ %... and flipped around vertical axis
+ [-1~0~0~1~pbs@llx~pbs@urx~add~0]~concat
+ }{%flip around horizontal axis in portrait mode
+ [1~0~0~-1~0~pbs@lly~pbs@ury~add]~concat
+ }ifelse
+ }?pdfmark
+ }
+ }
+ \begin{picture}(0,0)\put(0,0){\box_use:N#5}\end{picture}
+ \pbs_special:n{~mark~/EP~pdfmark~grestore}
+ %insert additional dict entries (the Ghostscript way)
+ \str_if_eq:eeF{\l_tmpa_tl}{}{
+ \pbs_special:n{
+ product~(Ghostscript)~search~{
+ pop~pop~pop~
+ mark~{pbs@obj\int_use:N\g_pbs_obj_int}~<<\l_tmpa_tl>>~/PUT~pdfmark
+ }{pop}ifelse
+ }
+ }
+ \tl_gset:Nx\g_pbs_pdflastxform_tl{{pbs@obj\int_use:N\g_pbs_obj_int}}
+ \int_gincr:N\g_pbs_obj_int
+ \int_compare:nT{#2>\c_zero_int}{
+ %Form XObjects for use as annotation appearances require that
+ %dvips generated PostScript to be further processed with ps2pdf
+ %must not have the exaggerated dpi resolution resulting from dvips
+ %option `-Ppdf'.
+ \tl_if_exist:NF\g_pbs_dpiwarned_tl{
+ \tl_new:N\g_pbs_dpiwarned_tl
+ \pbs_at_end_dvi:n{
+ \special{ps::[nobreak]~SDict~begin~\pbs_dpiwarning:\space end}
+ }
+ }
+ }
+ \pbs_special:n{
+ true~setglobal~
+ globaldict~/pbs@xform@level~pbs@xform@level~1~sub~put~
+ false~setglobal~
+ grestore
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfrefxform:n{% #1: xform obj ID
+ %The /SP pdfmark for placement of Form XObjects works reliably only
+ %since gs-9.14. As gs-9.14 had some other TeX-related issues, we
+ %require 9.15.
+ \tl_if_exist:NF\g_pbs_gsoldwarned_tl{
+ \tl_new:N\g_pbs_gsoldwarned_tl
+ \pbs_at_end_dvi:n{
+ \special{ps::[nobreak]~SDict~begin~\pbs_gsoldwarning:\space end}
+ }
+ }
+ \pbs_special:n{
+ gsave~currentpoint~translate~1~\tl_use:c{scale_#1}~div~dup~scale~
+ mark~#1~/SP~pdfmark~grestore
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfximage:n{
+ \filename@parse{#1}
+ \tl_set:Nx\l_pbs_ext_tl{\tl_lower_case:n{\filename@ext}}
+ \bool_if:nTF{
+ \str_if_eq_p:Vn\l_pbs_ext_tl{ps}
+ ||\str_if_eq_p:Vn\l_pbs_ext_tl{eps}
+ }{
+ \pbs_special:n{
+ mark~/_objdef~{pbs@obj\int_use:N\g_pbs_obj_int}~/NI~pdfmark
+ }
+ \special{psfile=#1~hsize=0~vsize=0}
+ \pbs_special:n{
+ {
+ 0~0~1~[1~0~0~1~0~0]~{}~image~%empty dummy, in case #1 is not
+ }?pdfmark %a valid raster image file
+ }
+ \tl_gset:Nx\g_pbs_pdflastximage_tl{{pbs@obj\int_use:N\g_pbs_obj_int}}
+ \int_gincr:N\g_pbs_obj_int
+ }{
+ \msg_error:nnxxx{pdfbase}{wrong~image~resource}{#1}{dvips}{
+ Postscript~(ps/eps)~with~bitmapped~content
+ }
+ }
+ }
+
+ \cs_new_protected_nopar:Nn\pbs_pdfcatalog:n{
+ \pbs_special:n{~mark~{Catalog}~<<#1>>~/PUT~pdfmark}
+ }
+
+ %marked content BDC/EMC operators
+ %require Ghostscript v. >= 9.15
+ \cs_new_protected_nopar:Nn\pbs_pdfbdc:nn{
+ \pbs_special:n{~mark~#1~#2~/BDC~pdfmark}
+ \tl_if_exist:NF\g_pbs_gsoldwarned_tl{
+ \tl_new:N\g_pbs_gsoldwarned_tl
+ \pbs_at_end_dvi:n{
+ \special{ps::[nobreak]~SDict~begin~\pbs_gsoldwarning:\space end}
+ }
+ }
+ }
+ \cs_new_nopar:Nn\pbs_pdfemc:{\pbs_special:n{~mark~/EMC~pdfmark}}
+ }
+ }
+}
+
+\cs_new_nopar:Nn\pbs_pdflastobj:{\g_pbs_pdflastobj_tl}
+\cs_new_nopar:Nn\pbs_pdflastann:{\g_pbs_pdflastann_tl}
+\cs_new_nopar:Nn\pbs_pdflastxform:{\g_pbs_pdflastxform_tl}
+\cs_new_nopar:Nn\pbs_pdflastximage:{\g_pbs_pdflastximage_tl}
+
+%adding AcroForm dict to PDF Catalog
+\tl_new:N\g_pbs_fields_tl %takes object IDs of Fields (aka annots with
+\tl_new:N\g_pbs_form_fonts_tl %name tree of font resources used in text fields
+\pbs_at_end_dvi:n{
+ \tl_if_empty:NF\g_pbs_fields_tl{
+ \pbs_pdfobj:nnn{}{array}{\g_pbs_fields_tl}
+ \pbs_pdfcatalog:n{
+ /AcroForm~<<
+ /Fields~\pbs_pdflastobj:/NeedAppearances~false~
+ \tl_if_empty:NF\g_pbs_form_fonts_tl{
+ /DR~<</Font~<<\g_pbs_form_fonts_tl>> >>
+ }
+ >>
+ }
+ }
+}
+\cs_new_protected_nopar:Nn\pbs_appendtofields:n{
+ \tl_gput_left:Nx\g_pbs_fields_tl{#1\space}
+}
+
+%modify output routine for output box insertions
+\bool_new:N\l_pbs_is_vertical_bool
+\cs_set_eq:NN\pbs_outputpage_orig:\@outputpage
+\cs_set_nopar:Npn\@outputpage{
+ \int_gincr:N\g_pbs_page_int
+ \iow_now:Nx\@mainaux{
+ \token_to_str:N\pbs@newkey{pbs@last@page}{\int_use:N\g_pbs_page_int}
+ }
+ \box_if_vertical:cTF{@outputbox}{
+ \bool_set_true:N\l_pbs_is_vertical_bool
+ }{
+ \bool_set_false:N\l_pbs_is_vertical_bool
+ }
+ \hbox_set:Nn\@outputbox{
+ %begin of page
+ \hbox_overlap_right:n{\seq_map_inline:Nn\g_pbs_bop_seq{##1}}
+ \box_use_drop:N\@outputbox
+ %end of page
+ \hbox_overlap_right:n{\seq_map_inline:Nn\g_pbs_eop_seq{##1}}
+ }
+ \bool_if:NT\l_pbs_is_vertical_bool{
+ \vbox_set:Nn\@outputbox{\box_use_drop:N\@outputbox}
+ }
+ \pbs_outputpage_orig:
+}
+
+\RequirePackage{atbegshi}
+\AtBeginDocument{
+ \bool_new:N\g_pbs_ocgbase_loaded_bool
+ \@ifpackageloaded{ocgbase}{
+ \bool_gset_true:N\g_pbs_ocgbase_loaded_bool
+ }{}
+ \AtBeginShipout{
+ \AtBeginShipoutAddToBoxForeground{
+ %insert /Properties into current page's resources
+ \pbs_insert_properties_entry:
+ %end of dvi (last page)
+ \int_compare:nT{\pbs@last@page=\g_pbs_page_int}{
+ \seq_map_variable:NNn\g_pbs_eod_seq\l_tmpa_tl{\l_tmpa_tl}
+ }
+ }
+ \AtBeginShipoutUpperLeft{
+ % workaround for curious AR bug (pdf annot or link placed on
+ % OCG remains active when OCG is hidden)
+ % see https://forums.adobe.com/message/5787612#5787612
+ % This can be fixed by placing a dumb (non-interactive) Widget dummy
+ % somewhere on the page.
+ \bool_if:NT\g_pbs_ocgbase_loaded_bool{
+ \skip_horizontal:n{1bp}
+ \box_move_down:nn{1bp}{\vbox:n{
+ \pbs_pdfannot:nnnn{3bp}{\c_zero_dim}{3bp}{
+ /Ff~65537/FT/Btn/Subtype/Widget
+ /T~(pbs@ARFix@\int_use:N\g_pbs_page_int)
+ }
+ }}
+ }
+ }
+ }
+}
+
+\group_begin:
+\char_set_catcode_active:N\+\let+\space
+\cs_new_nopar:Nx\pbs_gsoldwarning:{
+ {product~(Ghostscript)~search~{pop~pop~pop~true}{pop~false}ifelse~
+ revision~915~lt~and~{
+ (\token_to_str:N\n
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n
+ @@++++++++++++Warning:+Ghostscript+too+old!++++++++++++@@\token_to_str:N\n
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n
+ @@+++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+Ghostscript+version+>=+9.15.+required!++++++++++++++@@\token_to_str:N\n
+ @@+++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+Various+advanced+PDF+features+such+as+Layers+(OCGs)+@@\token_to_str:N\n
+ @@+and+animations+may+not+work.++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+Get+current+version+from++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+http://www.ghostscript.com/download+++++++++++++++++@@\token_to_str:N\n
+ @@+++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n)
+ print}~if}~?pdfmark
+}
+
+\cs_new_nopar:Nx\pbs_dpiwarning:{
+ {Resolution~1200~gt~VResolution~1200~gt~or~product~(Ghostscript)~
+ search~{pop~pop~pop~true}{pop~false}ifelse~and~{
+ (\token_to_str:N\n
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n
+ @@+++++Warning:+DVI+resolution+greater+than+1200+dpi!+++++@@\token_to_str:N\n
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n
+ @@++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+PDF+Annotation+appearances+(buttons,+animation+frames)+@@\token_to_str:N\n
+ @@+may+be+poorly+scaled,+clipped+or+invisible.++++++++++++@@\token_to_str:N\n
+ @@++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+Dvips+should+be+called+either+without+option+`-Ppdf':++@@\token_to_str:N\n
+ @@++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+++dvips+\jobname\token_to_str:N\n
+ @@++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+or+with+a+different+resolution+setting,+e.g.:++++++++++@@\token_to_str:N\n
+ @@++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@+++dvips+-Ppdf+-D1200+\jobname\token_to_str:N\n
+ @@++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@\token_to_str:N\n
+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\token_to_str:N\n)
+ print}~if}~?pdfmark
+}
+\group_end:
diff --git a/macros/latex/contrib/media9/players/APlayer.mxml b/macros/latex/contrib/media9/players/APlayer.mxml
new file mode 100644
index 0000000000..b03f04a4fc
--- /dev/null
+++ b/macros/latex/contrib/media9/players/APlayer.mxml
@@ -0,0 +1,429 @@
+<?xml version="1.0"?>
+<!-- -->
+<!-- Apache Flex 4 source file of APlayer.swf, -->
+<!-- a FlashPlayer-10 compatible component for playing -->
+<!-- MP3 audio files and streams. -->
+<!-- -->
+<!-- version 20150601 -->
+<!-- -->
+<!-- -->
+<!-- The free Apache Flex 4 SDK is required to compile -->
+<!-- this file. Get it from -->
+<!-- -->
+<!-- http://flex.apache.org/download-binaries.html -->
+<!-- -->
+<!-- and run -->
+<!-- -->
+<!-- mxmlc -static-link-runtime-shared-libraries APlayer.mxml -->
+<!-- -->
+<!-- on the command line. -->
+<!-- -->
+<!-- -->
+<!-- Copyright (C) 2012-today Alexander Grahn -->
+<!-- -->
+<!-- This work may be distributed and/or modified under the -->
+<!-- conditions of the LaTeX Project Public License. -->
+<!-- -->
+<!-- The latest version of this license is in -->
+<!-- http://www.latex-project.org/lppl.txt -->
+<!-- -->
+<!-- This work has the LPPL maintenance status `maintained'. -->
+<!-- -->
+<!-- The current maintainer of this work is A. Grahn. -->
+<!-- -->
+
+<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
+ xmlns:s="library://ns.adobe.com/flex/spark"
+ xmlns:mx="library://ns.adobe.com/flex/mx"
+ preinitialize="initialise(this.parameters);"
+ applicationComplete="initSound();addEventListeners();
+ fadeTargets=new Array(playProgress, caption);"
+ creationComplete="initCallBacks();initContext();"
+ mouseDown="pause();setFocus();"
+ mouseUp="play();"
+ backgroundAlpha="0"
+>
+
+ <fx:Script>
+ <![CDATA[
+ [Bindable] private var source:String;
+ [Bindable] private var policy:String=null;
+ [Bindable] private var autoPlay:Boolean=false;
+ [Bindable] private var loop:Boolean=false;
+ [Bindable] private var vol:Number=0.75;
+ [Bindable] private var pan:Number=0;
+ [Bindable] private var hideBar:Boolean=false;
+ [Bindable] private var fadeTargets:Array;
+ [Bindable] private var txtAlign:String;
+
+ private var snd:Sound;
+ private var id3:ID3Info;
+ private var sndCh:SoundChannel;
+ private var sndTr:SoundTransform;
+ private var playResumePos:Number = 0;
+ private var pauseAtPos:Number = -1;
+ private var toBePaused:Boolean = false;
+ private var plyng:Boolean = false;
+ private var muted:Boolean = false;
+ private var lastVol:Number;
+
+ private var deltaSeek:Number;
+ private var curTime:Number;
+ private var keyPressed:Boolean=false;
+ private var mouseIsOver:Boolean=false;
+
+ private function initialise(flashVars:Object):void {
+ source=flashVars.source;
+ if(flashVars.policy) policy=flashVars.policy;
+ if(flashVars.autoPlay=='true') autoPlay=true;
+ if(flashVars.loop=='true') loop=true;
+ if(flashVars.volume) vol=Number(flashVars.volume);
+ if(flashVars.balance) pan=Number(flashVars.balance);
+ if(flashVars.hideBar=='true') hideBar=true;
+ }
+
+ import mx.controls.Alert;
+
+ private function initSound():void {
+ snd = new Sound(); id3=null;
+ sndTr = new SoundTransform(vol, pan);
+ snd.addEventListener(Event.ID3, onID3Info);
+ snd.addEventListener(IOErrorEvent.IO_ERROR, onError);
+ snd.addEventListener(ProgressEvent.PROGRESS, onProgress);
+ snd.addEventListener(Event.COMPLETE, onComplete);
+ if(policy) Security.loadPolicyFile(policy);
+ snd.load(new URLRequest(source), new SoundLoaderContext(1000, true));
+ if(autoPlay) play();
+ }
+
+ private function onMouseOver(e:MouseEvent):void {
+ mouseIsOver=true;
+ fadeEffect.end();
+ playProgress.alpha=caption.alpha=1.0;
+ }
+
+ private function onMouseOut(e:MouseEvent):void {
+ mouseIsOver=false;
+ fadeEffect.play();
+ }
+
+ private function onKeyDown(e:KeyboardEvent):void {
+ switch(e.keyCode) {
+ case 32: //space bar
+ playPause();
+ break;
+ case 36: //home
+ pause();
+ playResumePos=(0);
+ break;
+ case 35: //end
+ if(playProgress.indeterminate) break;
+ pause();
+ playResumePos=(snd.length);
+ break;
+ case 37: //<--
+ fadeEffect.end();
+ playProgress.alpha=caption.alpha=1.0;
+ if(e.ctrlKey){
+ pan=Math.max(-1,pan-0.025);
+ balance(pan);
+ break;
+ }
+ if(!keyPressed){
+ deltaSeek=Math.max(100,snd.length/10000);
+ if(plyng)
+ curTime=sndCh.position;
+ else
+ curTime=playResumePos;
+ }
+ keyPressed=true;
+ playResumePos=Math.max(0,curTime-deltaSeek);
+ if(plyng) seek(playResumePos/1000);
+ deltaSeek*=1.1;
+ break;
+ case 39: //-->
+ fadeEffect.end();
+ playProgress.alpha=caption.alpha=1.0;
+ if(e.ctrlKey){
+ pan=Math.min(1,pan+0.025);
+ balance(pan);
+ break;
+ }
+ if(!keyPressed){
+ deltaSeek=Math.max(100,snd.length/10000);
+ if(plyng)
+ curTime=sndCh.position;
+ else
+ curTime=playResumePos;
+ }
+ keyPressed=true;
+ playResumePos=Math.min(snd.length-10,curTime+deltaSeek);
+ seek(playResumePos/1000);
+ deltaSeek*=1.1;
+ break;
+ case 38:
+ vol=Math.min(1,vol+0.025);
+ volume(vol);
+ break;
+ case 40:
+ if(e.ctrlKey){
+ pan=0;
+ balance(pan);
+ break;
+ }
+ vol=Math.max(0,vol-0.025);
+ volume(vol);
+ break;
+ default:
+ if(e.charCode==109) mute(); //`m'
+ }
+ }
+
+ private function onKeyUp(e:KeyboardEvent):void {
+ switch(e.keyCode) {
+ case 37: //<--
+ case 39: //-->
+ deltaSeek=Math.max(100,snd.length/10000);
+ keyPressed=false;
+ if (!mouseIsOver) fadeEffect.play();
+ break;
+ }
+ }
+
+ private function formatLabel(s:Number):String {
+ s/=1000;
+ var hrs:Number = Math.floor(s / 3600);
+ var min:Number = Math.floor(s / 60 % 60);
+ var sec:Number = Math.floor(s % 60);
+ var fmtd:String='';
+ if(hrs>0) fmtd = String(hrs)+':';
+ if(hrs>0 && min <10) fmtd+='0';
+ fmtd += String(min)+':';
+ if(sec<10) fmtd+='0';
+ fmtd += String(sec);
+ txtAlign="center";
+ try{
+ if(id3.songName)
+ fmtd += ' '+ String.fromCharCode(0x2014) + ' ' + id3.songName;
+ if(id3.artist) fmtd += ' | ' + id3.artist;
+ if(id3.album) fmtd += ' | ' + id3.album;
+ if(id3) txtAlign="start";
+ }catch(e:Object){}
+ return fmtd;
+ }
+
+ private function addEventListeners():void {
+ this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
+ this.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
+ this.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
+ this.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
+ this.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
+ }
+
+ private function setSource(s:String):void {
+ pause();
+ source=s;
+ initSound();
+ pause();seek(0);
+ }
+
+ private function loadPolicy(p:String):void {policy=p;}
+
+ private function play(p:Number=-1):void {
+ if(p>=0) seek(p);
+ if(plyng) return;
+ try{sndCh = snd.play(playResumePos, 0, sndTr);}
+ catch(e:Error){Alert.show(e.message,'Error');}
+ sndCh.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
+ plyng = true;
+ }
+
+ private function pause(p:Number=-1):void {
+ if(p>=0){pauseAtPos=p*1000;return;}
+ if(!plyng) return;
+ playResumePos = sndCh.position;
+ sndCh.stop();
+ plyng = false;
+ }
+
+ private function playPause():void {
+ if(plyng) pause(); else play();
+ }
+
+ private function seek(p:Number):void {
+ playResumePos = p*1000;
+ if(!plyng) return;
+ sndCh.stop();
+ plyng = false;
+ play();
+ }
+
+ private function rewind():void {
+ seek(0);
+ }
+
+ private function volume(v:Number):void {
+ sndTr.volume = v;
+ sndCh.soundTransform=sndTr;
+ }
+
+ private function mute():void {
+ if(muted) {
+ if(lastVol==0) lastVol=0.75;
+ volume(lastVol);
+ muted=false;
+ }
+ else {
+ lastVol = sndTr.volume
+ volume(0);
+ muted=true;
+ }
+ }
+
+ private function balance(p:Number):void {
+ sndTr.pan = p;
+ sndCh.soundTransform=sndTr;
+ }
+
+ private function currentTime():Number {
+ return sndCh.position/1000;
+ }
+
+ private function playing():Boolean {
+ return plyng;
+ }
+
+ private function duration():Number {
+ return snd.length/1000;
+ }
+
+ private function ismuted():Boolean {
+ return muted;
+ }
+
+ private function initCallBacks():void {
+ ExternalInterface.addCallback("play", play);
+ ExternalInterface.addCallback("pause", pause);
+ ExternalInterface.addCallback("playPause", playPause);
+ ExternalInterface.addCallback("seek", seek);
+ ExternalInterface.addCallback("rewind", rewind);
+ ExternalInterface.addCallback("volume", volume);
+ ExternalInterface.addCallback("balance", balance);
+ ExternalInterface.addCallback("mute", mute);
+ ExternalInterface.addCallback("setSource", setSource);
+ ExternalInterface.addCallback("loadPolicy", loadPolicy);
+ ExternalInterface.addCallback("currentTime", currentTime);
+ ExternalInterface.addCallback("duration", duration);
+ ExternalInterface.addCallback("playing", playing);
+ ExternalInterface.addCallback("muted", ismuted);
+ }
+
+ private function initContext():void {
+ this.contextMenu.hideBuiltInItems();
+
+ var itemPlayPause:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemPlayPause);
+ itemPlayPause.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{playPause();});
+
+ var itemRewind:ContextMenuItem = new ContextMenuItem("Rewind, [Home]");
+ this.contextMenu.customItems.push(itemRewind);
+ itemRewind.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{pause();playResumePos=(0);});
+
+ var itemGotoEnd:ContextMenuItem=new ContextMenuItem("Goto End, [End]");
+ this.contextMenu.customItems.push(itemGotoEnd);
+ itemGotoEnd.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{
+ if(playProgress.indeterminate) return;
+ pause();playResumePos=(snd.length);});
+
+ var itemMute:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemMute);
+ itemMute.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{mute();});
+
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("via keyboard:", true, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Seek, [\u2190]/[\u2192]", false, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Volume, [\u2191]/[\u2193]", false, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Balance, [Ctrl]+[\u2190]/[\u2193]/[\u2192]",
+ false, false, true));
+
+ this.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT,
+ function(e:ContextMenuEvent):void{
+ itemPlayPause.caption=(plyng ? "Pause" : "Play")+", [Space]";
+ itemMute.caption=(muted ? "Unmute" : "Mute")+", [m]";}
+ );
+ }
+
+ private function onSoundComplete(e:Event):void {
+ plyng = false;
+ playResumePos = 0;
+ if(loop) play();
+ }
+
+ private function onComplete(e:Event):void {
+ playProgress.indeterminate=false;
+ }
+
+ private function onProgress(e:ProgressEvent):void {
+ playProgress.indeterminate=true;
+ }
+
+ private function onEnterFrame(e:Event):void {
+ if(!playProgress.indeterminate) {
+ if(plyng) {
+ playProgress.setProgress(sndCh.position,snd.length);
+ caption.text=formatLabel(sndCh.position);
+ } else {
+ playProgress.setProgress(playResumePos,snd.length);
+ caption.text=formatLabel(playResumePos);
+ }
+ }else{
+ if(plyng) {
+ playProgress.setProgress(Math.random(),1);
+ caption.text=formatLabel(sndCh.position);
+ } else {
+ playProgress.setProgress(snd.bytesLoaded,snd.bytesTotal);
+ caption.text=formatLabel(playResumePos);
+ }
+ }
+ if(plyng&&pauseAtPos>=0&&sndCh.position<pauseAtPos)
+ toBePaused=true;
+ if(
+ plyng&&pauseAtPos>=0&&
+ sndCh.position>=pauseAtPos&&toBePaused
+ ){
+ pause();
+ pauseAtPos=-1;
+ toBePaused=false;
+ }
+ }
+
+ private function onID3Info(e:Event):void {id3 = e.target.id3;}
+
+ private function onError(e:ErrorEvent):void {
+ Alert.show(e.type, e.text);
+ }
+ ]]>
+ </fx:Script>
+
+ <fx:Declarations>
+ <s:Fade id="fadeEffect" targets="{fadeTargets}" alphaFrom="1.0" alphaTo="0"
+ duration="2000"/>
+ </fx:Declarations>
+
+ <mx:ProgressBar width="100%" height="100%" mode="manual"
+ labelPlacement="center" label="" id="playProgress"
+ visible="{!hideBar}"
+ />
+
+ <s:Label id="caption" width="100%" height="100%" verticalCenter="0"
+ paddingTop="2" fontWeight="bold" verticalAlign="middle" visible="{!hideBar}"
+ textAlign="{txtAlign}" paddingLeft="5"
+ />
+</s:Application>
diff --git a/macros/latex/contrib/media9/players/APlayer.swf b/macros/latex/contrib/media9/players/APlayer.swf
new file mode 100644
index 0000000000..7aea76a9d8
--- /dev/null
+++ b/macros/latex/contrib/media9/players/APlayer.swf
Binary files differ
diff --git a/macros/latex/contrib/media9/players/APlayer9.mxml b/macros/latex/contrib/media9/players/APlayer9.mxml
new file mode 100644
index 0000000000..9ea40902df
--- /dev/null
+++ b/macros/latex/contrib/media9/players/APlayer9.mxml
@@ -0,0 +1,417 @@
+<?xml version="1.0"?>
+<!-- -->
+<!-- Adobe Flex 3 source file of APlayer9.swf, -->
+<!-- a FlashPlayer-9 compatible component for playing -->
+<!-- MP3 audio files and streams. -->
+<!-- -->
+<!-- version 20150601 -->
+<!-- -->
+<!-- Copyright (C) 2012-today Alexander Grahn -->
+<!-- -->
+<!-- This work may be distributed and/or modified under the -->
+<!-- conditions of the LaTeX Project Public License. -->
+<!-- -->
+<!-- The latest version of this license is in -->
+<!-- http://www.latex-project.org/lppl.txt -->
+<!-- -->
+<!-- This work has the LPPL maintenance status `maintained'. -->
+<!-- -->
+<!-- The current maintainer of this work is A. Grahn. -->
+<!-- -->
+
+<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
+ preinitialize="initialise(this.parameters);"
+ applicationComplete="initSound();addEventListeners();"
+ creationComplete="initCallBacks();initContext();"
+ mouseDown="pause();setFocus();"
+ mouseUp="play();"
+ backgroundAlpha="0"
+ paddingTop="0" paddingBottom="0"
+ paddingLeft="0" paddingRight="0"
+ layout="absolute" clipContent="false"
+>
+
+ <mx:Script>
+ <![CDATA[
+ [Bindable] private var source:String;
+ [Bindable] private var policy:String=null;
+ [Bindable] private var autoPlay:Boolean=false;
+ [Bindable] private var loop:Boolean=false;
+ [Bindable] private var vol:Number=0.75;
+ [Bindable] private var pan:Number=0;
+ [Bindable] private var hideBar:Boolean=false;
+ [Bindable] private var txtAlign:String;
+
+ private var snd:Sound;
+ private var id3:ID3Info;
+ private var sndCh:SoundChannel;
+ private var sndTr:SoundTransform;
+ private var playResumePos:Number = 0;
+ private var pauseAtPos:Number = -1;
+ private var toBePaused:Boolean = false;
+ private var plyng:Boolean = false;
+ private var muted:Boolean = false;
+ private var lastVol:Number;
+
+ private var deltaSeek:Number;
+ private var curTime:Number;
+ private var keyPressed:Boolean=false;
+ private var mouseIsOver:Boolean=false;
+
+ import flash.external.*;
+ private function initialise(flashVars:Object):void {
+ source=flashVars.source;
+ if(flashVars.policy) policy=flashVars.policy;
+ if(flashVars.autoPlay=='true') autoPlay=true;
+ if(flashVars.loop=='true') loop=true;
+ if(flashVars.volume) vol=Number(flashVars.volume);
+ if(flashVars.balance) pan=Number(flashVars.balance);
+ if(flashVars.hideBar=='true') hideBar=true;
+ }
+
+ import mx.controls.Alert;
+
+ private function initSound():void {
+ snd = new Sound(); id3=null;
+ sndTr = new SoundTransform(vol, pan);
+ snd.addEventListener(Event.ID3, onID3Info);
+ snd.addEventListener(IOErrorEvent.IO_ERROR, onError);
+ snd.addEventListener(ProgressEvent.PROGRESS, onProgress);
+ snd.addEventListener(Event.COMPLETE, onComplete);
+ if(policy) Security.loadPolicyFile(policy);
+ snd.load(new URLRequest(source), new SoundLoaderContext(1000, true));
+ if(autoPlay) play();
+ }
+
+ private function onMouseOver(e:MouseEvent):void {
+ mouseIsOver=true;
+ fadeEffect.end();
+ playProgress.alpha=caption.alpha=1.0;
+ caption.visible=!hideBar;
+ }
+
+ private function onMouseOut(e:MouseEvent):void {
+ mouseIsOver=false;
+ fadeEffect.play();
+ }
+
+ private function onKeyDown(e:KeyboardEvent):void {
+ switch(e.keyCode) {
+ case 32: //space bar
+ playPause();
+ break;
+ case 36: //home
+ pause();
+ playResumePos=(0);
+ break;
+ case 35: //end
+ if(playProgress.indeterminate) break;
+ pause();
+ playResumePos=(snd.length);
+ break;
+ case 37: //<--
+ fadeEffect.end();
+ playProgress.alpha=caption.alpha=1.0;
+ caption.visible=!hideBar;
+ if(e.ctrlKey){
+ pan=Math.max(-1,pan-0.025);
+ balance(pan);
+ break;
+ }
+ if(!keyPressed){
+ deltaSeek=Math.max(100,snd.length/10000);
+ if(plyng)
+ curTime=sndCh.position;
+ else
+ curTime=playResumePos;
+ }
+ keyPressed=true;
+ playResumePos=Math.max(0,curTime-deltaSeek);
+ if(plyng) seek(playResumePos/1000);
+ deltaSeek*=1.1;
+ break;
+ case 39: //-->
+ fadeEffect.end();
+ playProgress.alpha=caption.alpha=1.0;
+ caption.visible=!hideBar;
+ if(e.ctrlKey){
+ pan=Math.min(1,pan+0.025);
+ balance(pan);
+ break;
+ }
+ if(!keyPressed){
+ deltaSeek=Math.max(100,snd.length/10000);
+ if(plyng)
+ curTime=sndCh.position;
+ else
+ curTime=playResumePos;
+ }
+ keyPressed=true;
+ playResumePos=Math.min(snd.length-10,curTime+deltaSeek);
+ seek(playResumePos/1000);
+ deltaSeek*=1.1;
+ break;
+ case 38:
+ vol=Math.min(1,vol+0.025);
+ volume(vol);
+ break;
+ case 40:
+ if(e.ctrlKey){
+ pan=0;
+ balance(pan);
+ break;
+ }
+ vol=Math.max(0,vol-0.025);
+ volume(vol);
+ break;
+ default:
+ if(e.charCode==109) mute(); //`m'
+ }
+ }
+
+ private function onKeyUp(e:KeyboardEvent):void {
+ switch(e.keyCode) {
+ case 37: //<--
+ case 39: //-->
+ deltaSeek=Math.max(100,snd.length/10000);
+ keyPressed=false;
+ if (!mouseIsOver) fadeEffect.play();
+ break;
+ }
+ }
+
+ private function formatLabel(s:Number):String {
+ s/=1000;
+ var hrs:Number = Math.floor(s / 3600);
+ var min:Number = Math.floor(s / 60 % 60);
+ var sec:Number = Math.floor(s % 60);
+ var fmtd:String='';
+ if(hrs>0) fmtd = String(hrs)+':';
+ if(hrs>0 && min <10) fmtd+='0';
+ fmtd += String(min)+':';
+ if(sec<10) fmtd+='0';
+ fmtd += String(sec);
+ txtAlign="center";
+ try{
+ if(id3.songName)
+ fmtd += ' '+ String.fromCharCode(0x2014) + ' ' + id3.songName;
+ if(id3.artist) fmtd += ' | ' + id3.artist;
+ if(id3.album) fmtd += ' | ' + id3.album;
+ if(id3) txtAlign="start";
+ }catch(e:Object){}
+ return fmtd;
+ }
+
+ private function addEventListeners():void {
+ this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
+ this.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
+ this.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
+ this.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
+ this.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
+ }
+
+ private function setSource(s:String):void {
+ pause();
+ source=s;
+ initSound();
+ pause();seek(0);
+ }
+
+ private function loadPolicy(p:String):void {policy=p;}
+
+ private function play(p:Number=-1):void {
+ if(p>=0) seek(p);
+ if(plyng) return;
+ try{sndCh = snd.play(playResumePos, 0, sndTr);}
+ catch(e:Error){Alert.show(e.message,'Error');}
+ sndCh.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
+ plyng = true;
+ }
+
+ private function pause(p:Number=-1):void {
+ if(p>=0){pauseAtPos=p*1000;return;}
+ if(!plyng) return;
+ playResumePos = sndCh.position;
+ sndCh.stop();
+ plyng = false;
+ }
+
+ private function playPause():void {
+ if(plyng) pause(); else play();
+ }
+
+ private function seek(p:Number):void {
+ playResumePos = p*1000;
+ if(!plyng) return;
+ sndCh.stop();
+ plyng = false;
+ play();
+ }
+
+ private function rewind():void {
+ seek(0);
+ }
+
+ private function volume(v:Number):void {
+ sndTr.volume = v;
+ sndCh.soundTransform=sndTr;
+ }
+
+ private function mute():void {
+ if(muted) {
+ if(lastVol==0) lastVol=0.75;
+ volume(lastVol);
+ muted=false;
+ }
+ else {
+ lastVol = sndTr.volume
+ volume(0);
+ muted=true;
+ }
+ }
+
+ private function balance(p:Number):void {
+ sndTr.pan = p;
+ sndCh.soundTransform=sndTr;
+ }
+
+ private function currentTime():Number {
+ return sndCh.position/1000;
+ }
+
+ private function playing():Boolean {
+ return plyng;
+ }
+
+ private function duration():Number {
+ return snd.length/1000;
+ }
+
+ private function ismuted():Boolean {
+ return muted;
+ }
+
+ private function initCallBacks():void {
+ ExternalInterface.addCallback("play", play);
+ ExternalInterface.addCallback("pause", pause);
+ ExternalInterface.addCallback("playPause", playPause);
+ ExternalInterface.addCallback("seek", seek);
+ ExternalInterface.addCallback("rewind", rewind);
+ ExternalInterface.addCallback("volume", volume);
+ ExternalInterface.addCallback("balance", balance);
+ ExternalInterface.addCallback("mute", mute);
+ ExternalInterface.addCallback("setSource", setSource);
+ ExternalInterface.addCallback("loadPolicy", loadPolicy);
+ ExternalInterface.addCallback("currentTime", currentTime);
+ ExternalInterface.addCallback("duration", duration);
+ ExternalInterface.addCallback("playing", playing);
+ ExternalInterface.addCallback("muted", ismuted);
+ }
+
+ private function initContext():void {
+ this.contextMenu.hideBuiltInItems();
+
+ var itemPlayPause:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemPlayPause);
+ itemPlayPause.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{playPause();});
+
+ var itemRewind:ContextMenuItem = new ContextMenuItem("Rewind, [Home]");
+ this.contextMenu.customItems.push(itemRewind);
+ itemRewind.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{pause();playResumePos=(0);});
+
+ var itemGotoEnd:ContextMenuItem=new ContextMenuItem("Goto End, [End]");
+ this.contextMenu.customItems.push(itemGotoEnd);
+ itemGotoEnd.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{
+ if(playProgress.indeterminate) return;
+ pause();playResumePos=(snd.length);});
+
+ var itemMute:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemMute);
+ itemMute.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{mute();});
+
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("via keyboard:", true, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Seek, [\u2190]/[\u2192]", false, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Volume, [\u2191]/[\u2193]", false, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Balance, [Ctrl]+[\u2190]/[\u2193]/[\u2192]",
+ false, false, true));
+
+ this.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT,
+ function(e:ContextMenuEvent):void{
+ itemPlayPause.caption=(plyng ? "Pause" : "Play")+", [Space]";
+ itemMute.caption=(muted ? "Unmute" : "Mute")+", [m]";}
+ );
+ }
+
+ private function onSoundComplete(e:Event):void {
+ plyng = false;
+ playResumePos = 0;
+ if(loop) play();
+ }
+
+ private function onComplete(e:Event):void {
+ playProgress.indeterminate=false;
+ }
+
+ private function onProgress(e:ProgressEvent):void {
+ playProgress.indeterminate=true;
+ }
+
+ private function onEnterFrame(e:Event):void {
+ if(!playProgress.indeterminate) {
+ if(plyng) {
+ playProgress.setProgress(sndCh.position,snd.length);
+ caption.text=formatLabel(sndCh.position);
+ } else {
+ playProgress.setProgress(playResumePos,snd.length);
+ caption.text=formatLabel(playResumePos);
+ }
+ }else{
+ if(plyng) {
+ playProgress.setProgress(Math.random(),1);
+ caption.text=formatLabel(sndCh.position);
+ } else {
+ playProgress.setProgress(snd.bytesLoaded,snd.bytesTotal);
+ caption.text=formatLabel(playResumePos);
+ }
+ }
+ if(plyng&&pauseAtPos>=0&&sndCh.position<pauseAtPos)
+ toBePaused=true;
+ if(
+ plyng&&pauseAtPos>=0&&
+ sndCh.position>=pauseAtPos&&toBePaused
+ ){
+ pause();
+ pauseAtPos=-1;
+ toBePaused=false;
+ }
+ }
+
+ private function onID3Info(e:Event):void {id3 = e.target.id3;}
+
+ private function onError(e:ErrorEvent):void {
+ Alert.show(e.type, e.text);
+ }
+ ]]>
+ </mx:Script>
+
+ <mx:Fade id="fadeEffect" target="{playProgress}" alphaFrom="1.0" alphaTo="0"
+ effectEnd="caption.visible=false" duration="2000"/>
+
+ <mx:ProgressBar width="100%" height="100%" mode="manual"
+ labelPlacement="center" label="" id="playProgress"
+ visible="{!hideBar}"
+ />
+
+ <mx:Label id="caption" width="100%" verticalCenter="0"
+ paddingTop="2" fontWeight="bold" visible="{!hideBar}"
+ textAlign="{txtAlign}" paddingLeft="5"
+ />
+</mx:Application>
diff --git a/macros/latex/contrib/media9/players/APlayer9.swf b/macros/latex/contrib/media9/players/APlayer9.swf
new file mode 100644
index 0000000000..eb26630025
--- /dev/null
+++ b/macros/latex/contrib/media9/players/APlayer9.swf
Binary files differ
diff --git a/macros/latex/contrib/media9/players/BSD-License b/macros/latex/contrib/media9/players/BSD-License
new file mode 100644
index 0000000000..67b0fe7164
--- /dev/null
+++ b/macros/latex/contrib/media9/players/BSD-License
@@ -0,0 +1,32 @@
+The BSD License
+
+Copyright (c) 2010, Adobe Systems Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems Incorporated, nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/macros/latex/contrib/media9/players/SMPfixes.patch b/macros/latex/contrib/media9/players/SMPfixes.patch
new file mode 100644
index 0000000000..04e48f4128
--- /dev/null
+++ b/macros/latex/contrib/media9/players/SMPfixes.patch
@@ -0,0 +1,158 @@
+--- ./player/StrobeMediaPlayback/src/org/osmf/player/chrome/widgets/PlayButtonOverlay.as.orig 2012-11-14 15:48:22.000000000 +0100
++++ ./player/StrobeMediaPlayback/src/org/osmf/player/chrome/widgets/PlayButtonOverlay.as 2012-11-20 15:14:02.000000000 +0100
+@@ -98,6 +98,11 @@
+ override protected function onMouseClick(event:MouseEvent):void
+ {
+ var playable:PlayTrait = media.getTrait(MediaTraitType.PLAY) as PlayTrait;
++ var seekable:SeekTrait = media.getTrait(MediaTraitType.SEEK) as SeekTrait;
++ if(playable.playState == PlayState.STOPPED)
++ {
++ try{seekable.seek(0);}catch(e:Object){}
++ }
+ playable.play();
+ }
+
+@@ -134,4 +139,4 @@
+ /* static */
+ private static const VISIBILITY_DELAY:int = 500;
+ }
+-}
+\ No newline at end of file
++}
+--- ./player/StrobeMediaPlayback/src/org/osmf/player/chrome/widgets/Slider.as.orig 2012-11-14 15:48:22.000000000 +0100
++++ ./player/StrobeMediaPlayback/src/org/osmf/player/chrome/widgets/Slider.as 2012-11-14 15:42:53.000000000 +0100
+@@ -106,7 +106,7 @@
+ if (_enabled && _sliding == false)
+ {
+ _sliding = true;
+- stage.addEventListener(MouseEvent.MOUSE_UP, onStageExitDrag);
++ stage.addEventListener(MouseEvent.MOUSE_UP, onStageExitDrag, true);
+ updateFace(down);
+ scrubTimer.start();
+ dispatchEvent(new ScrubberEvent(ScrubberEvent.SCRUB_START));
+--- ./player/StrobeMediaPlayback/src/org/osmf/player/chrome/widgets/PlayButton.as.orig 2012-11-14 15:48:22.000000000 +0100
++++ ./player/StrobeMediaPlayback/src/org/osmf/player/chrome/widgets/PlayButton.as 2012-11-14 15:42:53.000000000 +0100
+@@ -26,6 +26,7 @@
+ import org.osmf.traits.MediaTraitType;
+ import org.osmf.traits.PlayState;
+ import org.osmf.traits.PlayTrait;
++ import org.osmf.traits.SeekTrait;
+
+ public class PlayButton extends PlayableButton
+ {
+@@ -44,6 +45,11 @@
+ override protected function onMouseClick(event:MouseEvent):void
+ {
+ var playable:PlayTrait = media.getTrait(MediaTraitType.PLAY) as PlayTrait;
++ var seekable:SeekTrait = media.getTrait(MediaTraitType.SEEK) as SeekTrait;
++ if(playable.playState == PlayState.STOPPED)
++ {
++ try{seekable.seek(0);}catch(e:Object){}
++ }
+ playable.play();
+ event.stopImmediatePropagation();
+ }
+@@ -58,4 +64,4 @@
+ }
+ }
+ }
+-}
+\ No newline at end of file
++}
+--- ./player/StrobeMediaPlayback/src/StrobeMediaPlayback.as.orig 2012-11-14 15:48:22.000000000 +0100
++++ ./player/StrobeMediaPlayback/src/StrobeMediaPlayback.as 2012-11-14 15:42:54.000000000 +0100
+@@ -52,6 +52,7 @@
+ import org.osmf.traits.MediaTraitType;
+ import org.osmf.traits.PlayState;
+ import org.osmf.traits.PlayTrait;
++ import org.osmf.traits.SeekTrait;
+ import org.osmf.utils.OSMFSettings;
+ import org.osmf.utils.OSMFStrings;
+
+@@ -152,6 +153,7 @@
+
+ player.addEventListener(TimeEvent.COMPLETE, onComplete);
+ player.addEventListener(MediaErrorEvent.MEDIA_ERROR, onMediaError);
++ player.addEventListener(MediaPlayerCapabilityChangeEvent.CAN_PLAY_CHANGE, onCanPlayChange);
+
+ // Add DRM error handler
+ var drmManager:DRMManager = DRMManager.getDRMManager();
+@@ -338,6 +340,8 @@
+ mainContainer.backgroundAlpha = 0;
+ mainContainer.addEventListener(MouseEvent.DOUBLE_CLICK, onFullScreenRequest);
+ mainContainer.addEventListener(MouseEvent.CLICK, onMainClick, false);
++ mainContainer.addEventListener(MouseEvent.MOUSE_DOWN, onMainDown, false);
++ mainContainer.addEventListener(MouseEvent.MOUSE_UP, onMainUp, false);
+ mainContainer.doubleClickEnabled = true;
+
+ addChild(mainContainer);
+@@ -415,6 +419,9 @@
+ }
+
+ player.addEventListener(PlayEvent.PLAY_STATE_CHANGE, onSetAutoHide);
++
++ controlBarContainer.addEventListener(MouseEvent.MOUSE_DOWN, onControlBarAny, false);
++ controlBarContainer.addEventListener(MouseEvent.MOUSE_UP, onControlBarAny, false);
+
+ layout();
+
+@@ -757,6 +764,38 @@
+ }
+ }
+
++ private function onMainDown(event:MouseEvent):void
++ {
++ var playable:PlayTrait = player.media.getTrait(MediaTraitType.PLAY) as PlayTrait;
++ if (playable.playState == PlayState.PLAYING)
++ {
++ playable.pause();
++ }
++ }
++
++ private function onMainUp(event:MouseEvent):void
++ {
++ var playable:PlayTrait = player.media.getTrait(MediaTraitType.PLAY) as PlayTrait;
++ var seekable:SeekTrait = player.media.getTrait(MediaTraitType.SEEK) as SeekTrait;
++ if(playable.playState == PlayState.STOPPED)
++ {
++ if(player.canSeek){seekable.seek(0);}
++ playable.play();
++ }
++ else if(playable.playState == PlayState.PAUSED)
++ {
++ playable.play();
++ }
++ }
++
++ private function onControlBarAny(event:MouseEvent):void
++ {
++ if(event.eventPhase == EventPhase.BUBBLING_PHASE)
++ {
++ event.stopImmediatePropagation();
++ }
++ }
++
+ /**
+ * Toggles full screen state.
+ */
+@@ -1036,12 +1075,20 @@
+ && configuration.endOfVideoOverlay != ""
+ && player.loop == false
+ && player.playing == false
++ && player.autoRewind == true
+ )
+ {
+ processPoster(configuration.endOfVideoOverlay);
+ }
+ }
+
++ private function onCanPlayChange(event:MediaPlayerCapabilityChangeEvent):void
++ {
++ // Make sure this event gets handled only once:
++ player.removeEventListener(event.type, arguments.callee);
++ player.play(); player.stop();
++ }
++
+ private function onMediaError(event:MediaErrorEvent):void
+ {
+ // Make sure this event gets handled only once:
diff --git a/macros/latex/contrib/media9/players/SlideShow.mxml b/macros/latex/contrib/media9/players/SlideShow.mxml
new file mode 100644
index 0000000000..c55c41fd13
--- /dev/null
+++ b/macros/latex/contrib/media9/players/SlideShow.mxml
@@ -0,0 +1,482 @@
+<?xml version="1.0"?>
+<!-- -->
+<!-- Apache Flex 4 source file of SlideShow.swf, -->
+<!-- a FlashPlayer-10 compatible component for -->
+<!-- JPEG/PNG/GIF slideshows. -->
+<!-- -->
+<!-- version 20150718 -->
+<!-- -->
+<!-- -->
+<!-- The free Apache Flex 4 SDK is required to compile -->
+<!-- this file. Get it from -->
+<!-- -->
+<!-- http://flex.apache.org/download-binaries.html -->
+<!-- -->
+<!-- and run -->
+<!-- -->
+<!-- mxmlc -static-link-runtime-shared-libraries SlideShow.mxml -->
+<!-- -->
+<!-- on the command line. -->
+<!-- -->
+<!-- -->
+<!-- Copyright (C) 2015-today Alexander Grahn -->
+<!-- -->
+<!-- This work may be distributed and/or modified under the -->
+<!-- conditions of the LaTeX Project Public License. -->
+<!-- -->
+<!-- The latest version of this license is in -->
+<!-- http://www.latex-project.org/lppl.txt -->
+<!-- -->
+<!-- This work has the LPPL maintenance status `maintained'. -->
+<!-- -->
+<!-- The current maintainer of this work is A. Grahn. -->
+<!-- -->
+<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
+ xmlns:s="library://ns.adobe.com/flex/spark"
+ xmlns:mx="library://ns.adobe.com/flex/mx"
+ preinitialize="initialise(this.parameters);"
+ applicationComplete="initApp();"
+ resize="if(dynResolution){dynResolution=false;forcedByUsr=true;setImgSrc();}"
+ backgroundAlpha="0"
+>
+ <fx:Script>
+ <![CDATA[
+ import mx.managers.CursorManager;
+
+ private var slideshowXML:XML;
+ private var setXMLDone:Boolean;
+ private var isEnabled:Boolean=true;
+
+ private function setXML(s:String):void {
+ var loader:URLLoader = new URLLoader();
+ loader.addEventListener(Event.COMPLETE, onURLLoaderComplete);
+ loader.addEventListener(IOErrorEvent.IO_ERROR, onAnyError);
+ loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onAnyError);
+ isEnabled=false;
+ slideshowXML=null;
+ setXMLDone=false;
+ isIOError=true;
+ CursorManager.setBusyCursor();
+ timeoutTimer.start();
+ loader.load(new URLRequest(s));
+ }
+
+ private function onURLLoaderComplete(event:Event):void
+ {
+ CursorManager.removeBusyCursor();
+ isEnabled=true;
+ var loader:URLLoader = event.target as URLLoader;
+ if (loader != null)
+ {
+ slideshowXML = new XML(loader.data);
+ setXMLDone=true;
+ rotations=new Object();
+ seek(0);
+ if(autoPlay) play();
+ }
+ }
+
+ [Bindable] private var delay:Number=5;
+ [Bindable] private var autoPlay:Boolean=false;
+ private var xmlfile:String=null;
+
+ private function initialise(flashVars:Object):void {
+ xmlfile=flashVars.xml;
+ if(flashVars.delay){delay=flashVars.delay}
+ if(flashVars.autoPlay=='true'){autoPlay=true}
+ }
+
+ private var reloadTimer:Timer = null;
+ private var playTimer:Timer = null;
+ private var timeoutTimer:Timer;
+ private var curFile:String;
+
+ private var idx:Number=0;
+ private var urlIdx:Number=0;
+ [Bindable] private var src1:String;
+ [Bindable] private var src2:String;
+
+ private var rotations:Object;
+
+ private function rotate(n:Number=1):void {
+ if(Img1.visible){
+ n==0 ? Img1.rotation=0 : Img1.rotation+=90*Math.floor(n);
+ Img1.rotation%=360;
+ rotations[slideshowXML.Img[idx].URL[urlIdx].toString()]=
+ Img1.rotation/90;
+ } else {
+ n==0 ? Img2.rotation=0 : Img2.rotation+=90*Math.floor(n);
+ Img2.rotation%=360;
+ rotations[slideshowXML.Img[idx].URL[urlIdx].toString()]=
+ Img2.rotation/90;
+ }
+ }
+
+ import mx.collections.ArrayCollection;
+ import spark.collections.Sort;
+ import spark.collections.SortField;
+
+ private var forcedByUsr:Boolean;
+ private var dynResolution:Boolean;
+
+ private function setImgSrc():void {
+ if(reloadTimer && reloadTimer.hasEventListener(TimerEvent.TIMER)){
+ reloadTimer.removeEventListener(TimerEvent.TIMER, reloadImg);
+ reloadTimer=null;
+ }
+ if(setXMLDone && slideshowXML && (isEnabled||forcedByUsr)){
+ isEnabled=false;
+ forcedByUsr=false;
+ CursorManager.removeBusyCursor();
+ CursorManager.setBusyCursor();
+ timeoutTimer.start();
+ isIOError=true;
+
+ urlIdx=0;
+ if(slideshowXML.Img[idx].URL.length()>1){
+ var sizes:ArrayCollection = new ArrayCollection();
+ for(var i:Number=0; i<slideshowXML.Img[idx].URL.length(); i++){
+ var sizeStr:String=slideshowXML.Img[idx].URL[i].attribute("size");
+ if(sizeStr.match(/^\s*[0-9]+x[0-9]+\s*$/)){
+ var xsize:Number=Number(sizeStr.replace(/x[0-9]+/, ""));
+ var ysize:Number=Number(sizeStr.replace(/[0-9]+x/, ""));
+ sizes.addItem(
+ {size: xsize*ysize, index: i, x: xsize, y: ysize});
+ }
+ }
+ if(sizes.length>0){
+ dynResolution=true;
+ var sort:Sort = new Sort();
+ sort.fields=[new SortField("size",true,true)];
+ sizes.sort=sort;// ^--descending order
+ sizes.refresh();
+
+ urlIdx=sizes.getItemAt(0).index;
+ for(i=0; i<sizes.length; i++){
+ if(
+ sizes.getItemAt(i).x >= this.width ||
+ sizes.getItemAt(i).y >= this.height
+ ) urlIdx=sizes.getItemAt(i).index;
+ }
+ }
+ }
+
+ curFile=slideshowXML.Img[idx].URL[urlIdx].toString();
+
+ var pattern:RegExp;
+ if(slideshowXML.Img[idx].URL[urlIdx].attribute('height').length() > 0){
+ pattern = new RegExp(
+ slideshowXML.Img[idx].URL[urlIdx].attribute('height'), "g");
+ if(curFile.search(pattern)>-1){
+ dynResolution=true;
+ curFile=curFile.replace(pattern, this.height.toString());
+ }
+ }
+
+ if(slideshowXML.Img[idx].URL[urlIdx].attribute('width').length() > 0){
+ pattern = new RegExp(
+ slideshowXML.Img[idx].URL[urlIdx].attribute('width'), "g");
+ if(curFile.search(pattern)>-1){
+ dynResolution=true;
+ curFile=curFile.replace(pattern, this.width.toString());
+ }
+ }
+
+ if(Img1.visible) {
+ src2=null; src2=curFile;
+ } else {
+ src1=null; src1=curFile;
+ }
+ }
+ if(slideshowXML.Img[idx].attribute('live')>0){
+ reloadTimer=new Timer(slideshowXML.Img[idx].attribute('live')*1000,1);
+ reloadTimer.addEventListener(TimerEvent.TIMER, reloadImg);
+ reloadTimer.start();
+ }
+ }
+
+ private var isReload:Boolean;
+ private function reloadImg(e:TimerEvent):void {
+ isReload=true; setImgSrc();
+ }
+
+ private var isPlaying:Boolean=false;
+
+ private function playPause():void {
+ if(isPlaying) pause(); else play();
+ }
+
+ private function play():void {
+ if(!isPlaying && slideshowXML.Img.length()>1) {
+ if(!playTimer){
+ playTimer=new Timer(delay*1000, 0);
+ playTimer.addEventListener(TimerEvent.TIMER, nextSlide);
+ }
+ isPlaying=true;
+ playTimer.start();
+ }
+ }
+
+ private function pause():void {
+ if(isPlaying) {
+ isPlaying=false;
+ playTimer.stop();
+ }
+ }
+
+ private function nextSlide(e:Event=null):void {
+ if(!isEnabled && !forcedByUsr) return;
+ if(isPlaying) playTimer.stop();
+ var evnt:MouseEvent = e as MouseEvent;
+ if(evnt && evnt.type=="mouseUp" && evnt.shiftKey) {
+ prevSlide();
+ } else {
+ idx++;
+ if(idx==slideshowXML.Img.length()) idx=0;
+ isReload=false;
+ setImgSrc();
+ }
+ }
+
+ private function prevSlide(e:MouseEvent=null):void {
+ if(!isEnabled && !forcedByUsr) return;
+ if(isPlaying) playTimer.stop();
+ idx--;
+ if(idx<0) idx=slideshowXML.Img.length()-1;
+ isReload=false;
+ setImgSrc();
+ }
+
+ private function seek(i:Number):void {
+ if(!isEnabled && !forcedByUsr) return;
+ if(isPlaying) playTimer.stop();
+ if(i<0 || i>=slideshowXML.Img.length())
+ idx=slideshowXML.Img.length()-1;
+ else
+ idx=Math.floor(i);
+ isReload=false;
+ setImgSrc();
+ }
+
+ private function onKeyDown(e:KeyboardEvent):void {
+ forcedByUsr=true;
+ switch(e.keyCode) {
+ case 32: //space bar
+ playPause();
+ break;
+ case 36: //home
+ if(isPlaying) pause();
+ seek(0);
+ break;
+ case 35: //end
+ if(isPlaying) pause();
+ seek(-1);
+ break;
+ case 37: //<--
+ prevSlide();
+ break;
+ case 39: //-->
+ nextSlide();
+ break;
+ case 82: //`r'
+ e.shiftKey ? rotate(-1) : rotate();
+ break;
+ }
+ }
+
+ private function slideNum():Number {return idx;}
+
+ private function numSlides():Number {return slideshowXML.Img.length();}
+
+ private function playing():Boolean {return isPlaying;}
+
+ private function initApp():void {
+ this.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
+ ExternalInterface.addCallback("setXML", setXML);
+ ExternalInterface.addCallback("play",
+ function():void{forcedByUsr=true;play();});
+ ExternalInterface.addCallback("pause",
+ function():void{forcedByUsr=true;pause();});
+ ExternalInterface.addCallback("playPause",
+ function():void{forcedByUsr=true;playPause();});
+ ExternalInterface.addCallback("seek",
+ function(i:Number):void{forcedByUsr=true;seek(i);});
+ ExternalInterface.addCallback("slideNum", slideNum);
+ ExternalInterface.addCallback("numSlides", numSlides);
+ ExternalInterface.addCallback("playing", playing);
+ ExternalInterface.addCallback("rotate", rotate);
+
+ var itemNextSlide:ContextMenuItem =
+ new ContextMenuItem("Next Slide, [\u2192]");
+ this.contextMenu.customItems.push(itemNextSlide);
+ itemNextSlide.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{forcedByUsr=true;nextSlide();});
+
+ var itemPrevSlide:ContextMenuItem =
+ new ContextMenuItem("Previous Slide, [\u2190]");
+ this.contextMenu.customItems.push(itemPrevSlide);
+ itemPrevSlide.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{forcedByUsr=true;prevSlide();});
+
+ var itemPlayPause:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemPlayPause);
+ itemPlayPause.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{forcedByUsr=true;playPause();});
+
+ var itemFirstSlide:ContextMenuItem =
+ new ContextMenuItem("First Slide, [Home]");
+ this.contextMenu.customItems.push(itemFirstSlide);
+ itemFirstSlide.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{
+ forcedByUsr=true;if(isPlaying) pause();seek(0);});
+
+ var itemLastSlide:ContextMenuItem =
+ new ContextMenuItem("Last Slide, [End]");
+ this.contextMenu.customItems.push(itemLastSlide);
+ itemLastSlide.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{
+ forcedByUsr=true;if(isPlaying) pause();seek(-1);});
+
+ var itemRotRight:ContextMenuItem =
+ new ContextMenuItem("Rotate Right, [r]");
+ this.contextMenu.customItems.push(itemRotRight);
+ itemRotRight.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{forcedByUsr=true;rotate();});
+
+ var itemRotLeft:ContextMenuItem =
+ new ContextMenuItem("Rotate Left, [\u21e7]+[r]");
+ this.contextMenu.customItems.push(itemRotLeft);
+ itemRotLeft.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{forcedByUsr=true;rotate(-1);});
+
+ this.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT,
+ function(e:ContextMenuEvent):void{
+ itemPlayPause.caption=(isPlaying ? "Pause" : "Play")+", [Space]";
+ itemPlayPause.visible=itemNextSlide.visible=itemPrevSlide.visible=
+ itemFirstSlide.visible=itemLastSlide.visible=false;
+ if(slideshowXML.Img.length()>1){
+ itemPlayPause.visible=itemNextSlide.visible=itemPrevSlide.visible=
+ itemFirstSlide.visible=itemLastSlide.visible=true;
+ }
+ });
+
+ timeoutTimer=new Timer(30000, 1);
+ timeoutTimer.addEventListener(TimerEvent.TIMER, onAnyError);
+ curFile=xmlfile; if(xmlfile) setXML(xmlfile);
+ CursorManager.hideCursor();
+ }
+
+ private var isIOError:Boolean;
+ private function onAnyError(e:Event):void {
+ if(e.type!=IOErrorEvent.IO_ERROR || setXMLDone==false){
+ isIOError=false;
+ setXMLDone=true;
+ if(Img1.visible){
+ src2=null; src2="/dev/null"
+ }else{
+ src1=null; src1="/dev/null"
+ }
+ caption.text=e.type+': '+curFile;
+ if(e.type==TimerEvent.TIMER)
+ caption.text="timeout after 30 s: "+curFile;
+ CursorManager.removeBusyCursor();
+ }else{
+ if(isIOError) caption.text=e.type+': '+curFile;
+ onImgDone(e);
+ }
+ caption.visible=true;
+ if(reloadTimer && reloadTimer.hasEventListener(TimerEvent.TIMER)){
+ reloadTimer.removeEventListener(TimerEvent.TIMER, reloadImg);
+ reloadTimer=null; isReload=false;
+ }
+ }
+
+ private function onImgDone(e:Event):void {
+ timeoutTimer.reset();
+ if(e.target==Img1){
+ Img1.visible=true;Img2.visible=false;
+ } else {
+ Img2.visible=true;Img1.visible=false;
+ }
+ if(slideshowXML.Img.length()>1){
+ slideNo.text=String(idx+1)+"/"+slideshowXML.Img.length().toString();
+ slideNo.visible=slideNo.includeInLayout=true;
+ } else {
+ slideNo.visible=slideNo.includeInLayout=false;
+ }
+ var curRot:Number=0;
+ if (rotations.hasOwnProperty(slideshowXML.Img[idx].URL[urlIdx].toString()))
+ curRot=rotations[slideshowXML.Img[idx].URL[urlIdx].toString()];
+ else
+ curRot=Number(slideshowXML.Img[idx].attribute('rot90'));
+ rotate(0);
+ if(e.type==FlexEvent.READY) {
+ rotate(curRot);
+ if(slideshowXML.Img[idx].caption.toString()!=""){
+ caption.text=slideshowXML.Img[idx].caption.toString();
+ caption.visible=true;
+ } else {
+ caption.text="";
+ caption.visible=false;
+ }
+ if(!isReload) anim.play();
+ if(slideshowXML.Img[idx].attribute('live')>0)
+ cache.removeCacheEntry(curFile);
+ }
+ CursorManager.removeBusyCursor();
+ isEnabled=true;
+ if(isPlaying && !playTimer.running) playTimer.start();
+ }
+ ]]>
+ </fx:Script>
+
+ <fx:Declarations>
+ <s:Animate id="anim" target="{grpCaption}">
+ <s:motionPaths>
+ <s:SimpleMotionPath property="bottom"
+ valueFrom="{-grpCaption.height}" valueTo="0"/>
+ </s:motionPaths>
+ </s:Animate>
+ <s:ContentCache id="cache"/>
+ </fx:Declarations>
+
+ <s:Image id="Img1" source="{src1}" visible="false"
+ width="100%" height="100%"
+ horizontalCenter="0" verticalCenter="0"
+ contentLoader="{cache}"
+ ready="onImgDone(event)"
+ ioError="onAnyError(event)"
+ securityError="onAnyError(event)"
+ smooth="true"
+ />
+
+ <s:Image id="Img2" source="{src2}" visible="true"
+ width="100%" height="100%"
+ horizontalCenter="0" verticalCenter="0"
+ contentLoader="{cache}"
+ ready="onImgDone(event)"
+ ioError="onAnyError(event)"
+ securityError="onAnyError(event)"
+ smooth="true"
+ />
+
+ <s:Group id="grpCaption" width="100%" bottom="{-grpCaption.height}">
+ <s:Label id="caption" left="0" bottom="0" visible="false"
+ paddingLeft="2" paddingBottom="2" paddingRight="2" paddingTop="4"
+ backgroundColor="0xdddddd" backgroundAlpha="0.7"
+ maxWidth="{grpCaption.width-slideNo.width}"
+ maxDisplayedLines="{-1}" lineBreak="toFit"
+ />
+ <s:Label id="slideNo" right="0" bottom="0" visible="false"
+ paddingLeft="2" paddingBottom="2" paddingRight="2" paddingTop="4"
+ backgroundColor="0xdddddd" backgroundAlpha="0.7"
+ />
+ </s:Group>
+
+ <s:Button alpha="0" width="100%" height="100%"
+ mouseUp="forcedByUsr=true;nextSlide(event);"
+ mouseOver="anim.play();CursorManager.showCursor();"
+ mouseOut="if(anim.isPlaying) anim.reverse(); else anim.play(null,true);
+ CursorManager.hideCursor();"
+ />
+</s:Application>
diff --git a/macros/latex/contrib/media9/players/SlideShow.swf b/macros/latex/contrib/media9/players/SlideShow.swf
new file mode 100644
index 0000000000..acd00c7a03
--- /dev/null
+++ b/macros/latex/contrib/media9/players/SlideShow.swf
Binary files differ
diff --git a/macros/latex/contrib/media9/players/StrobeMediaPlayback-license b/macros/latex/contrib/media9/players/StrobeMediaPlayback-license
new file mode 100644
index 0000000000..4a3d2ec8c0
--- /dev/null
+++ b/macros/latex/contrib/media9/players/StrobeMediaPlayback-license
@@ -0,0 +1,5 @@
+Strobe Media Playback is covered under the BSD Licence. See file BSD-License
+
+OSMF and NetMocker are covered under version 1.1 of the MPL Licence. You may obtain a copy of the License at http://www.mozilla.org/MPL/
+
+The font we used by the player, Playback Sans, is released under the SIL Open Font License - see file player/StrobeMediaPlayback/assets/font/PlaybackSansEULA.txt
diff --git a/macros/latex/contrib/media9/players/StrobeMediaPlayback.swf b/macros/latex/contrib/media9/players/StrobeMediaPlayback.swf
new file mode 100644
index 0000000000..58e4170588
--- /dev/null
+++ b/macros/latex/contrib/media9/players/StrobeMediaPlayback.swf
Binary files differ
diff --git a/macros/latex/contrib/media9/players/VPlayer.mxml b/macros/latex/contrib/media9/players/VPlayer.mxml
new file mode 100644
index 0000000000..347a1058eb
--- /dev/null
+++ b/macros/latex/contrib/media9/players/VPlayer.mxml
@@ -0,0 +1,338 @@
+<?xml version="1.0"?>
+<!-- -->
+<!-- Apache Flex 4 source file of VPlayer.swf, -->
+<!-- a FlashPlayer-10 compatible component for playing -->
+<!-- FLV and MP4/H.264 video files and streams. -->
+<!-- -->
+<!-- version 20180118 -->
+<!-- -->
+<!-- -->
+<!-- The free Apache Flex 4 SDK is required to compile -->
+<!-- this file. Get it from -->
+<!-- -->
+<!-- http://flex.apache.org/download-binaries.html -->
+<!-- -->
+<!-- and run -->
+<!-- -->
+<!-- mxmlc -static-link-runtime-shared-libraries VPlayer.mxml -->
+<!-- -->
+<!-- on the command line. -->
+<!-- -->
+<!-- -->
+<!-- Copyright (C) 2012-today Alexander Grahn -->
+<!-- -->
+<!-- This work may be distributed and/or modified under the -->
+<!-- conditions of the LaTeX Project Public License. -->
+<!-- -->
+<!-- The latest version of this license is in -->
+<!-- http://www.latex-project.org/lppl.txt -->
+<!-- -->
+<!-- This work has the LPPL maintenance status `maintained'. -->
+<!-- -->
+<!-- The current maintainer of this work is A. Grahn. -->
+<!-- -->
+
+<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
+ xmlns:s="library://ns.adobe.com/flex/spark"
+ xmlns:mx="library://ns.adobe.com/flex/mx"
+ preinitialize="initialise(this.parameters);"
+ applicationComplete="initApp();"
+ backgroundAlpha="0"
+>
+ <fx:Script>
+ <![CDATA[
+ [Bindable] private var scaleMode:String='letterbox';
+ [Bindable] private var source:String;
+ [Bindable] private var autoPlay:Boolean=false;
+ [Bindable] private var autoRewind:Boolean=false;
+ [Bindable] private var loop:Boolean=false;
+ [Bindable] private var vol:Number=0.75;
+ private var vidComplete:Boolean=false;
+ private var deltaSeek:Number;
+ private var curTime:Number;
+ private var newPos:Number;
+ private var keyPressed:Boolean=false;
+ private var pauseAtPos:Number = -1;
+ private var toBePaused:Boolean = false;
+
+ private function initialise(flashVars:Object):void {
+ source=flashVars.source;
+ if(flashVars.autoPlay=='true'){autoPlay=true}
+ if(flashVars.autoRewind=='true'){autoRewind=true}
+ if(flashVars.loop=='true'){loop=true}
+ if(flashVars.volume){vol=Number(flashVars.volume)}
+ if(flashVars.scaleMode){scaleMode=flashVars.scaleMode}
+ }
+
+ private function onProgress(event:ProgressEvent):void {
+ if (event.bytesTotal) event.target.visible=true;
+ else event.target.visible=false;
+ }
+
+ import org.osmf.events.MediaPlayerStateChangeEvent;
+ import org.osmf.media.MediaPlayerState;
+ import mx.controls.Alert;
+ private function onStateChange(event:MediaPlayerStateChangeEvent):void {
+ vidComplete=false;
+ switch(event.state) {
+ case MediaPlayerState.PLAYBACK_ERROR: // ('playbackError')
+ Alert.show('Unable to play \''+event.target.source+'\'','Error');
+ break;
+ //case MediaPlayerState.READY:
+ // if(autoPlay) vidDisp.play();
+ // break;
+ //MediaPlayerState.BUFFERING
+ //MediaPlayerState.LOADING
+ //MediaPlayerState.PAUSED
+ //MediaPlayerState.PLAYING
+ //MediaPlayerState.UNINITIALIZED
+ }
+ }
+
+ private function onKeyDown(e:KeyboardEvent):void {
+ switch(e.keyCode) {
+ case 32: //space bar
+ playPause();
+ break;
+ case 36: //home
+ vidDisp.pause();
+ vidDisp.seek(0);
+ break;
+ case 35: //end
+ if(vidDisp.bytesTotal){
+ vidDisp.pause();
+ vidDisp.seek(vidDisp.duration-0.1);
+ }
+ break;
+ case 37: //<--
+ fadeEffect.end();
+ playProgress.alpha=0.5;
+ playProgress.visible=true;
+ if(!keyPressed){
+ deltaSeek=Math.max(1,vidDisp.duration/1000);
+ curTime=vidDisp.currentTime;
+ }
+ keyPressed=true;
+ newPos=Math.max(0,curTime-deltaSeek);
+ playProgress.setProgress(newPos,vidDisp.duration);
+ playProgress.label=formatTime(newPos);
+ vidDisp.seek(newPos);
+ deltaSeek*=1.1;
+ break;
+ case 39: //-->
+ fadeEffect.end();
+ playProgress.alpha=0.5;
+ playProgress.visible=true;
+ if(!keyPressed){
+ deltaSeek=Math.max(1,vidDisp.duration/1000);
+ curTime=vidDisp.currentTime;
+ }
+ keyPressed=true;
+ newPos=Math.min(vidDisp.duration-0.1,curTime+deltaSeek);
+ playProgress.setProgress(newPos,vidDisp.duration);
+ playProgress.label=formatTime(newPos);
+ vidDisp.seek(newPos);
+ deltaSeek*=1.1;
+ break;
+ case 38:
+ vol=Math.min(1,vol+0.025);
+ volume(vol);
+ break;
+ case 40:
+ vol=Math.max(0,vol-0.025);
+ volume(vol);
+ break;
+ default:
+ if(e.charCode==109) mute(); //`m'
+ }
+ }
+
+ private function onKeyUp(e:KeyboardEvent):void {
+ switch(e.keyCode) {
+ case 37: //<--
+ case 39: //-->
+ deltaSeek=Math.max(1,vidDisp.duration/1000);
+ keyPressed=false;
+ fadeEffect.play();
+ break;
+ }
+ }
+
+ private function onCurrentTimeChange(e:Event):void {
+ if(vidDisp.playing&&pauseAtPos>=0&&vidDisp.currentTime<pauseAtPos)
+ toBePaused=true;
+ if(
+ vidDisp.playing&&pauseAtPos>=0&&
+ vidDisp.currentTime>=pauseAtPos&&toBePaused
+ ){
+ pause();
+ pauseAtPos=-1;
+ toBePaused=false;
+ }
+ }
+
+ private function play(p:Number=-1):void {
+ if(p>=0) seek(p);
+ if(vidComplete){seek(0);}vidDisp.play();
+ }
+
+ private function pause(p:Number=-1):void {
+ if(p>=0){pauseAtPos=p;return;}
+ vidDisp.pause();
+ }
+
+ private function playPause():void {
+ if(vidDisp.playing) vidDisp.pause(); else vidDisp.play();
+ }
+
+ private function seek(p:Number):void {
+ vidDisp.seek(p);
+ }
+
+ private function rewind():void {
+ vidDisp.seek(0);
+ }
+
+ private function volume(v:Number):void {
+ vidDisp.muted=false;
+ vidDisp.volume = v;
+ }
+
+ private function mute():void {
+ if(vidDisp.muted) {
+ vidDisp.muted=false;
+ if (vidDisp.volume==0) volume(0.75);
+ }
+ else {
+ vidDisp.muted=true;
+ }
+ }
+
+ private function setSource(s:Object):void {
+ vidDisp.source = s;
+ }
+
+ private function formatTime(s:Number):String {
+ var hrs:Number = Math.floor(s / 3600);
+ var min:Number = Math.floor(s / 60 % 60);
+ var sec:Number = Math.floor(s % 60);
+
+ var fmtd:String='';
+
+ if (hrs>0) fmtd = String(hrs)+':';
+
+ if (hrs>0 && min <10) fmtd+='0';
+ fmtd += String(min)+':';
+
+ if (sec<10) fmtd+='0';
+ fmtd += String(sec);
+
+ return fmtd;
+ }
+
+ private function currentTime():Number {
+ return vidDisp.currentTime;
+ }
+
+ private function playing():Boolean {
+ return vidDisp.playing;
+ }
+
+ private function duration():Number {
+ return vidDisp.duration;
+ }
+
+ private function muted():Boolean {
+ return vidDisp.muted;
+ }
+
+ private function initApp():void {
+ this.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
+ this.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
+ ExternalInterface.addCallback("play", play);
+ ExternalInterface.addCallback("pause", pause);
+ ExternalInterface.addCallback("playPause", playPause);
+ ExternalInterface.addCallback("seek", seek);
+ ExternalInterface.addCallback("rewind", rewind);
+ ExternalInterface.addCallback("volume", volume);
+ ExternalInterface.addCallback("mute", mute);
+ ExternalInterface.addCallback("setSource", setSource);
+ ExternalInterface.addCallback("currentTime", currentTime);
+ ExternalInterface.addCallback("duration", duration);
+ ExternalInterface.addCallback("playing", playing);
+ ExternalInterface.addCallback("muted", muted);
+
+ var itemPlayPause:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemPlayPause);
+ itemPlayPause.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{playPause();});
+
+ var itemRewind:ContextMenuItem = new ContextMenuItem("Rewind, [Home]");
+ this.contextMenu.customItems.push(itemRewind);
+ itemRewind.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{vidDisp.pause();vidDisp.seek(0);});
+
+ var itemGotoEnd:ContextMenuItem = new ContextMenuItem("Goto End, [End]");
+ this.contextMenu.customItems.push(itemGotoEnd);
+ itemGotoEnd.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{vidDisp.pause();
+ vidDisp.seek(vidDisp.duration-0.1);});
+
+ var itemMute:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemMute);
+ itemMute.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{mute();});
+
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("via keyboard:", true, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Seek, [\u2190]/[\u2192]", false, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Volume, [\u2191]/[\u2193]", false, false, true));
+
+ this.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT,
+ function(e:ContextMenuEvent):void{
+ itemPlayPause.caption=(vidDisp.playing ? "Pause" : "Play")+", [Space]";
+ itemMute.caption=(vidDisp.muted ? "Unmute" : "Mute")+", [m]";}
+ );
+ }
+ ]]>
+ </fx:Script>
+
+ <fx:Declarations>
+ <s:Fade id="fadeEffect" target="{playProgress}" alphaFrom="0.5" alphaTo="0"
+ duration="2000"/>
+ </fx:Declarations>
+
+ <!-- autoPlay setting (autoPlay="{autoPlay}") is not reliable;
+ we handle this in MediaPlayerStateChange event -->
+ <s:VideoDisplay
+ id="vidDisp"
+ width="100%" height="100%" scaleMode="{scaleMode}"
+ source="{source}" volume="{vol}"
+ autoPlay="{autoPlay}"
+ autoRewind="{autoRewind}" loop="{loop}"
+ mediaPlayerStateChange="onStateChange(event);"
+ complete="vidComplete=true;"
+ durationChange="vidDisp.seek(0);"
+ currentTimeChange="onCurrentTimeChange(event);"
+ />
+
+ <mx:ProgressBar width="100%" mode="polled" source="vidDisp"
+ horizontalCenter="0" bottom="0" labelPlacement="center"
+ id="loadingProgress" alpha="0.5"
+ complete="loadingProgress.visible=false;"
+ progress="onProgress(event)"
+ />
+
+ <mx:ProgressBar width="100%" mode="manual"
+ horizontalCenter="0" bottom="0" labelPlacement="center"
+ id="playProgress" alpha="0.5"
+ visible="false"
+ />
+
+ <s:Button alpha="0" width="100%" height="100%"
+ mouseDown="vidDisp.pause();"
+ mouseUp="if(vidComplete) vidDisp.seek(0);vidDisp.play();"
+ />
+</s:Application>
diff --git a/macros/latex/contrib/media9/players/VPlayer.swf b/macros/latex/contrib/media9/players/VPlayer.swf
new file mode 100644
index 0000000000..835c290837
--- /dev/null
+++ b/macros/latex/contrib/media9/players/VPlayer.swf
Binary files differ
diff --git a/macros/latex/contrib/media9/players/VPlayer9.mxml b/macros/latex/contrib/media9/players/VPlayer9.mxml
new file mode 100644
index 0000000000..6741267f10
--- /dev/null
+++ b/macros/latex/contrib/media9/players/VPlayer9.mxml
@@ -0,0 +1,318 @@
+<?xml version="1.0"?>
+<!-- -->
+<!-- Adobe Flex 3 source file of VPlayer9.swf, -->
+<!-- a FlashPlayer-9 compatible component for playing -->
+<!-- FLV and MP4/H.264 video files and streams. -->
+<!-- -->
+<!-- version 20150729 -->
+<!-- -->
+<!-- Copyright (C) 2012-today Alexander Grahn -->
+<!-- -->
+<!-- This work may be distributed and/or modified under the -->
+<!-- conditions of the LaTeX Project Public License. -->
+<!-- -->
+<!-- The latest version of this license is in -->
+<!-- http://www.latex-project.org/lppl.txt -->
+<!-- -->
+<!-- This work has the LPPL maintenance status `maintained'. -->
+<!-- -->
+<!-- The current maintainer of this work is A. Grahn. -->
+<!-- -->
+
+<mx:Application
+ xmlns:mx="http://www.adobe.com/2006/mxml"
+ preinitialize="initialise(this.parameters);"
+ applicationComplete="initApp();"
+ backgroundAlpha="0"
+ paddingTop="0" paddingBottom="0"
+ paddingLeft="0" paddingRight="0"
+ layout="absolute" clipContent="false"
+>
+ <mx:Script>
+ <![CDATA[
+ [Bindable] private var source:String;
+ [Bindable] private var autoPlay:Boolean=false;
+ [Bindable] private var loop:Boolean=false;
+ [Bindable] private var vol:Number=0.75;
+ [Bindable] private var keepAspect:Boolean=true;
+ private var vidComplete:Boolean=false;
+ private var deltaSeek:Number;
+ private var playheadTime:Number;
+ private var newPos:Number;
+ private var keyPressed:Boolean=false;
+ private var muted:Boolean=false;
+ private var lastVolume:Number;
+ private var pauseAtPos:Number = -1;
+ private var toBePaused:Boolean = false;
+
+ import flash.external.*;
+ private function initialise(flashVars:Object):void {
+ source=flashVars.source;
+ if(flashVars.autoPlay=='true') autoPlay=true;
+ if(flashVars.loop=='true') loop=true;
+ if(flashVars.volume) vol=Number(flashVars.volume);
+ if(flashVars.scaleMode && flashVars.scaleMode!='letterbox') keepAspect=false;
+ }
+
+ private function onProgress(event:ProgressEvent):void {
+ if (event.bytesTotal) event.target.visible=true;
+ else event.target.visible=false;
+ }
+
+ import mx.events.VideoEvent;
+ //import flash.events.VideoEvent;
+ import mx.controls.Alert;
+ private function onStateChange(event:mx.events.VideoEvent):void {
+ vidComplete=false;
+ if(event.state=='connectionError')
+ Alert.show('Unable to play \''+event.target.source+'\'','Error');
+ }
+
+ private function onKeyDown(e:KeyboardEvent):void {
+ switch(e.keyCode) {
+ case 32: //space bar
+ playPause();
+ break;
+ case 36: //home
+ vidDisp.pause();
+ vidDisp.playheadTime=0;
+ break;
+ case 35: //end
+ if(vidDisp.bytesTotal){
+ vidDisp.pause();
+ vidDisp.playheadTime=vidDisp.totalTime-0.1;
+ }
+ break;
+ case 37: //<--
+ fadeEffect.end();
+ playProgress.alpha=0.5;
+ playProgress.visible=true;
+ if(!keyPressed){
+ deltaSeek=Math.max(1,vidDisp.totalTime/1000);
+ playheadTime=vidDisp.playheadTime;
+ }
+ keyPressed=true;
+ newPos=Math.max(0,playheadTime-deltaSeek);
+ playProgress.setProgress(newPos,vidDisp.totalTime);
+ playProgress.label=formatTime(newPos);
+ vidDisp.playheadTime=newPos;
+ deltaSeek*=1.1;
+ break;
+ case 39: //-->
+ fadeEffect.end();
+ playProgress.alpha=0.5;
+ playProgress.visible=true;
+ if(!keyPressed){
+ deltaSeek=Math.max(1,vidDisp.totalTime/1000);
+ playheadTime=vidDisp.playheadTime;
+ }
+ keyPressed=true;
+ newPos=Math.min(vidDisp.totalTime-0.1,playheadTime+deltaSeek);
+ playProgress.setProgress(newPos,vidDisp.totalTime);
+ playProgress.label=formatTime(newPos);
+ vidDisp.playheadTime=newPos;
+ deltaSeek*=1.1;
+ break;
+ case 38:
+ vol=Math.min(1,vol+0.025);
+ volume(vol);
+ break;
+ case 40:
+ vol=Math.max(0,vol-0.025);
+ volume(vol);
+ break;
+ default:
+ if(e.charCode==109) mute(); //`m'
+ }
+ }
+
+ private function onKeyUp(e:KeyboardEvent):void {
+ switch(e.keyCode) {
+ case 37: //<--
+ case 39: //-->
+ deltaSeek=Math.max(1,vidDisp.totalTime/1000);
+ keyPressed=false;
+ fadeEffect.play();
+ break;
+ }
+ }
+
+ private function onPlayheadUpdate(e:Event):void {
+ if(vidDisp.playing&&pauseAtPos>=0&&vidDisp.playheadTime<pauseAtPos)
+ toBePaused=true;
+ if(
+ vidDisp.playing&&pauseAtPos>=0&&
+ vidDisp.playheadTime>=pauseAtPos&&toBePaused
+ ){
+ pause();
+ pauseAtPos=-1;
+ toBePaused=false;
+ }
+ }
+
+ private function play(p:Number=-1):void {
+ if(p>=0) seek(p);
+ if(vidComplete){seek(0);}vidDisp.play();
+ }
+
+ private function pause(p:Number=-1):void {
+ if(p>=0){pauseAtPos=p;return;}
+ vidDisp.pause();
+ }
+
+ private function playPause():void {
+ if(vidDisp.playing) vidDisp.pause(); else vidDisp.play();
+ }
+
+ private function seek(p:Number):void {
+ vidDisp.playheadTime=p;
+ }
+
+ private function rewind():void {
+ vidDisp.playheadTime=0;
+ }
+
+ private function volume(v:Number):void {
+ muted=false;
+ vidDisp.volume = v;
+ }
+
+ private function mute():void {
+ if(muted) {
+ if (lastVolume==0) volume(0.75);
+ else volume(lastVolume);
+ }
+ else {
+ muted=true;lastVolume=vidDisp.volume;vidDisp.volume=0;
+ }
+ }
+
+ private function setSource(s:String):void {
+ vidDisp.pause();vidDisp.source = s;
+ vidDisp.play();vidDisp.pause();
+ vidDisp.playheadTime=0;
+ }
+
+ private function formatTime(s:Number):String {
+ var hrs:Number = Math.floor(s / 3600);
+ var min:Number = Math.floor(s / 60 % 60);
+ var sec:Number = Math.floor(s % 60);
+
+ var fmtd:String='';
+
+ if (hrs>0) fmtd = String(hrs)+':';
+
+ if (hrs>0 && min <10) fmtd+='0';
+ fmtd += String(min)+':';
+
+ if (sec<10) fmtd+='0';
+ fmtd += String(sec);
+
+ return fmtd;
+ }
+
+ private function currentTime():Number {
+ return vidDisp.playheadTime;
+ }
+
+ private function playing():Boolean {
+ return vidDisp.playing;
+ }
+
+ private function duration():Number {
+ return vidDisp.totalTime;
+ }
+
+ private function ismuted():Boolean {
+ return muted;
+ }
+
+ private function initApp():void {
+ this.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
+ this.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
+ ExternalInterface.addCallback("play", play);
+ ExternalInterface.addCallback("pause", pause);
+ ExternalInterface.addCallback("playPause", playPause);
+ ExternalInterface.addCallback("seek", seek);
+ ExternalInterface.addCallback("rewind", rewind);
+ ExternalInterface.addCallback("volume", volume);
+ ExternalInterface.addCallback("mute", mute);
+ ExternalInterface.addCallback("setSource", setSource);
+ ExternalInterface.addCallback("currentTime", currentTime);
+ ExternalInterface.addCallback("duration", duration);
+ ExternalInterface.addCallback("playing", playing);
+ ExternalInterface.addCallback("muted", ismuted);
+
+ var itemPlayPause:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemPlayPause);
+ itemPlayPause.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{playPause();});
+
+ var itemRewind:ContextMenuItem = new ContextMenuItem("Rewind, [Home]");
+ this.contextMenu.customItems.push(itemRewind);
+ itemRewind.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{vidDisp.pause();seek(0);});
+
+ var itemGotoEnd:ContextMenuItem = new ContextMenuItem("Goto End, [End]");
+ this.contextMenu.customItems.push(itemGotoEnd);
+ itemGotoEnd.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{if(vidDisp.bytesTotal){
+ vidDisp.pause();
+ vidDisp.playheadTime=vidDisp.totalTime-0.1;}});
+
+ var itemMute:ContextMenuItem = new ContextMenuItem("N.N.");
+ this.contextMenu.customItems.push(itemMute);
+ itemMute.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
+ function(e:ContextMenuEvent):void{mute();});
+
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("via keyboard:", true, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Seek, [\u2190]/[\u2192]", false, false, true));
+ this.contextMenu.customItems.push(
+ new ContextMenuItem("Volume, [\u2191]/[\u2193]", false, false, true));
+
+ this.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT,
+ function(e:ContextMenuEvent):void{
+ itemPlayPause.caption=(vidDisp.playing ? "Pause" : "Play")+", [Space]";
+ itemMute.caption=(muted ? "Unmute" : "Mute")+", [m]";}
+ );
+ }
+ ]]>
+ </mx:Script>
+
+ <mx:Fade id="fadeEffect" target="{playProgress}" alphaFrom="0.5" alphaTo="0" duration="2000"
+ effectEnd="playProgress.label=''"
+ />
+
+ <mx:VideoDisplay
+ id="vidDisp"
+ width="100%" height="100%" maintainAspectRatio="{keepAspect}"
+ backgroundAlpha="0" borderThickness="0"
+ source="{source}" volume="{vol}"
+ autoPlay="{autoPlay}" autoRewind="{loop}"
+ stateChange="onStateChange(event);"
+ complete="vidComplete=true;"
+ rewind="vidDisp.play();"
+ playheadUpdate="onPlayheadUpdate(event);"
+ />
+
+ <mx:ProgressBar width="100%" mode="polled" source="vidDisp"
+ horizontalCenter="0" bottom="0" labelPlacement="center"
+ id="loadingProgress" alpha="0.5"
+ complete="loadingProgress.visible=false;"
+ progress="onProgress(event)"
+ />
+
+ <mx:ProgressBar width="100%" mode="manual"
+ horizontalCenter="0" bottom="0" labelPlacement="center"
+ id="playProgress" alpha="0.5"
+ visible="false"
+ />
+
+ <mx:Button alpha="0" width="100%" height="100%" cornerRadius="0"
+ paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0"
+ mouseDown="vidDisp.pause();"
+ mouseUp="if(vidComplete) vidDisp.playheadTime=0;vidDisp.play();"
+ />
+</mx:Application>
diff --git a/macros/latex/contrib/media9/players/VPlayer9.swf b/macros/latex/contrib/media9/players/VPlayer9.swf
new file mode 100644
index 0000000000..c99e5a23f4
--- /dev/null
+++ b/macros/latex/contrib/media9/players/VPlayer9.swf
Binary files differ