diff options
Diffstat (limited to 'macros/luatex/optex/pdfextra/pdfextra.opm')
-rw-r--r-- | macros/luatex/optex/pdfextra/pdfextra.opm | 1728 |
1 files changed, 0 insertions, 1728 deletions
diff --git a/macros/luatex/optex/pdfextra/pdfextra.opm b/macros/luatex/optex/pdfextra/pdfextra.opm deleted file mode 100644 index bccf096484..0000000000 --- a/macros/luatex/optex/pdfextra/pdfextra.opm +++ /dev/null @@ -1,1728 +0,0 @@ -% vim: tw=80 -\_def\_pdfextra_version{0.2} -\_codedecl \RM {Extra PDF features (v\_pdfextra_version)} -\_namespace{pdfextra} - - \_doc - \sec Package initialization - - We ensure that hyperlinking is active. Our fallback \`\_linkcolor` must - exist. We also use it for `\hyperlinks` if the user didn't enable - `\hyperlinks` yet (we don't want to override user setting). - \_cod - -\_ifdefined\_ilinkcolor\_else - \_ifdefined\_linkcolor - \_ea\_let\_ea\_linkcolor \_ifdefined\Blue\Blue\_else\_empty\_fi - \_else - \_let\_linkcolor\linkcolor - \_fi -\_fi - -\_ifx\_dest\_destactive\_else - \_hyperlinks\_linkcolor\_linkcolor -\_fi - - \_doc - We are in the \OpTeX/ package namespace. A couple of shortcuts are defined - here: \`\.isdefined`, \`\.trycs`, \`\.cs` \`\.slet`, \`\.slet`, \`\.sdef` and - \`\.sxdef`. They all hard code the package name, because we already have too - many levels of indirection. - \_cod - -\_def\.isdefined#1{\_isdefined{_pdfextra_#1}} -\_def\.trycs#1{\_trycs{_pdfextra_#1}} -\_def\.cs#1{\_cs{_pdfextra_#1}} -\_def\.slet#1#2{\_slet{_pdfextra_#1}{_pdfextra_#2}} -\_def\.sdef#1{\_sdef{_pdfextra_#1}} -\_def\.sxdef#1{\_sxdef{_pdfextra_#1}} - - \_doc - \sec Helper macros - The macros here are just helpers for the macros to follow. They are not - useful generally, but proved useful in the expandable context of writing to - PDF files. - - Already the first one limits the use to \LuaTeX/ (but who needs other engines - anyways :). \`\.emptyor`<possibly empty text><text to use when first argument is - nonempty> checks whether the first argument is empty, if not it expands the - second argument which can use the text from the first argument with - \`\.nonempty`. \`\.attrorempty`<attribute name><value> builds upon the first - one and is really useful for PDF dictionaries, when we don't want to write an - attribute without a value (a default specified by standard will be used instead). - \_cod - -\_def\.emptyor#1#2{% - \_immediateassignment\_edef\.nonempty{#1}% - \_ifx\.nonempty\_empty\_else #2\_fi -} -\_def\.attrorempty#1#2{\.emptyor{#2}{/#1 \.nonempty}} - - \_doc - There is a dillema for handling colors. While typesetting it is possible to - use greyscale, CMYK or RGB colors. But there are contexts where it is - possible to only use RGB colors. We want to provide the user with two - possibilities of specifying colors: - - \begitems - * RGB color using PDF triplet (e.g. `1 0 0`), - * \OpTeX/ color using control sequence (e.g. `\Blue`) - \enditems - - Both are handled by \`\.colortorgbdef`<cs><color specification>, which - defines <cs> to the corresponding PDF RGB triplet. The indirection with - defining a macro is because we want to use the processed color within - expansion only contexts where grouping is not possible. - \_cod - -\_def\.colortorgbdef#1#2{\_bgroup - \_def\_setrgbcolor##1{##1}% - \_def\_setcmykcolor##1{\_cmyktorgb ##1 ;}% - \_def\_setgreycolor##1{##1 ##1 ##1}% - \_xdef#1{#2}% - \_egroup -} - - \_doc - \`\.xaddto``\macro`{<text>} is a natural extension of \OpTeX's `\addto` that - expands <text> and is global.\nl - \`\.tmp` is used throught the package for temporary values. - \_cod - -\_def\.xaddto#1#2{\_edef\.tmp{#2}% - \_global\_ea\_addto\_ea#1\_ea{\.tmp}% -} - - \_doc - This package defines a few commands in the form - `\macro[<name>][<optional arguments>]{<text>}`. To make it possible to omit - the `[<optional arguments>]` \`\.secondoptdef` is defined. - - `\.secondoptdef\<macro><parameters>{<body>}` - defines `\macro` with first mandatory argument in brackets (saved to - \`\.name`). Second optional argument in brackets is scanned using helper - macro defined with `\optdef` and is saved to `\_opt` token list). Additional - <parameters> can be specified as with `\optdef` (numbered from `#1`). - \_cod - -\_def\.secondoptdef#1{% - \_def#1[##1]{\_def\.name{##1}\.cs{sopt:\_string#1}}% - \_ea\_optdef\_csname _pdfextra_sopt:\_string#1\_endcsname[]% -} - - \_doc - When processing comma separated lists sometimes it is needed to ignore the - remaining text. For this we use \`\.untilend` macro which ignores everything - up to dummy \`\.end`. This is analogous to \OpTeX/'s `\_finbody` used for the - same purpose. Sometimes `\.end` is used as sentinel and compared in `\ifx` - tests, hence we define it to a unique value. - \_cod - -\_def\.untilend#1\.end{} -\_def\.end{_pdfextra_end} - - \_doc - For various uses it is necessary to know the number of page where something - happens. This has to be handled asynchronously with `\write`. Here we use - \OpTeX/ specific `.ref` file and associated macros, but this could be - replaced as long as the same interface is exposed. - - \`\.setpageof`<name> writes \`\.Xpageof`<name> to the `.ref` file. In the next - \TeX/ run `\.Xpageof` finds out the page number (`\gpageno`) from \OpTeX/'s - `\_currpage` and saves it so that \`\.pageof`<name> can retrieve it. In the - first run we can't be sure of the page where the content will end up. As a - rough estimate we take the current page~-- this actually works well for - slides where page breaks are manual. - - When `.ref` file is read along with the defintion of `\.Xpageof` this package - has not been loaded yet. Hence we can't use namespaced variants of - `\.isdefined`, etc. - \_cod - -\_refdecl{% - \_def\.Xpageof#1{\_isdefined{_pdfextra_pageof:#1}\_iffalse - \_sxdef{_pdfextra_pageof:#1}{\_ea\_ignoresecond\_currpage}\_fi - }% -} - -\_def\.setpageof#1{\_openref \_ewref\.Xpageof{{#1}}} - -\_def\.pageof#1{% - \.trycs{pageof:#1}{% - \_the\_numexpr\_gpageno+1\_relax % best effort = current page num - }% -} - - \_doc - \label[files] - \sec Handling of files - - Handling of files is a big topic of this package. Files are everywhere~-- - files containing multimedia, JavaScript script files, attachments, externally - referred files\dots Therefore a more sophisticated mechanism for handling - files is needed. The mechanism introduced in this section handles all three - cases of a {\em file specification}: - - \begitems - * files embedded in the PDF (\"e", embedded file), - * files determined by path (\"x", external file), - * files determined by URL (\"u", url file). - \enditems - - Although ideally all three would be interchangible this is not always the - case, because e.g. some media files must be embedded and linking to external - resources does not work with embedded files. - - In most cases there are two many names and other associated values involved: - \begitems - * Some kind of a \"friendly" name. This one is sometimes shown by PDF viewers. - * The real name of the file. Also shown but in different contexts. - * The path or URL used to determine the file. - * MIME type of the file. - \enditems - - For example when talking about \OpTeX/'s documentation we might have a - friendly name of \"opdoc", file name of \"`optex-doc.pdf`", URL of - \"\url{http://petr.olsak.net/ftp/olsak/optex/optex-doc.pdf}" and MIME type of - \"application/pdf". Different subset of them is required in different - contexts, but the user should only have to specify the friendly name (by - which they will refer to the file) and the path/URL of the file. The rest - will be deduced. The friendly name is used as a handle and {\em is usable} - in all places where file specification is required (although it may not - produce conforming output, see above). - - In this two step process~-- definition and (re)use~-- we introduce a command - for defining files: \`\filedef``/<type> [<friendly name>]{<path or URL>}`. The - macro itself does general definitions and dispatches the type dependant work - to other macros in the form `_filedef:<type>`. - \_cod - -\_def\.filedef/#1#2[#3]#4{% - \.sxdef{filename:#3}{(\.filename{#4})}% - \_edef\.tmp{\.exttomime{\.fileext{#4}}}% - \_ifx\.tmp\_empty - \_opwarning{MIME type of '#4' unknown, using '\.defaultmimetype'}% - \_edef\.tmp{\.defaultmimetype}% - \_fi - \.sxdef{filemime:#3}{\.tmp}% - \.cs{filedef:#1}{#3}{#4}% -} -\_nspublic \filedef ; - - \_doc - Types \"e", \"x", \"u" are predefined, anything else would essentialy be a - variant of these. - - External file (\"x") is determined only by path. - \_cod - -\.sdef{filedef:x}#1#2{% - \.slet{filespec:#1}{filename:#1}% -} - - \_doc - URL file (\"u") is determined by URL. Using all sorts of characters is - allowed by using `\_detokenize`. This time it is necessary to create full - {\em file specification}~-- a dictionary, where the \"file system" is URL. - \_cod - -\.sdef{filedef:u}#1#2{% - \.sdef{filespec:#1}{<</FS /URL /F (\_detokenize{#2})>>}% -} - - \_doc - Embedded files (\"e") are the most interesting ones. For further use (e.g. - for displaying the embedded files as attachments) MIME type is required. It - is saved in the stream as a `\Subtype`, encoded as a PDF name (e.g. - `/video#2Fmp4`). The embedded file stream must be wrapped in a full {\em file - specification}, which has the `/EF` (\"embedded file") entry. Also the - friendly name is used for some purpose by PDF viewers, so it set in `/Desc` - (description). - \_cod - -\.sdef{filedef:e}#1#2{% - \_edef\.tmp{\.cs{filemime:#1}}% - \_isfile{#2}\_iffalse - \_opwarning{file '#2' not found}% - \_fi - \_pdfobj stream - attr{/Type /EmbeddedFile /Subtype \_ea\.mimetoname\_ea[\.tmp]} - file {#2}% - \_pdfrefobj\_pdflastobj - \.sxdef{filestream:#1}{\_the\_pdflastobj\_space 0 R}% - \_pdfobj {<</Type /Filespec - /F \.cs{filename:#1} - /Desc (#1) - /EF << /F \_the\_pdflastobj \_space 0 R >>% - >>}% - \_pdfrefobj\_pdflastobj - \.sxdef{filespec:#1}{\_the\_pdflastobj\_space 0 R}% -} - - \_doc - Now the less interesting part~-- determining the file names from paths and - determining MIME types. The file name is the part after the last \"`/`" (if - any). The file extension is the part after last \"`.`" (if any). - \_cod - -\_def\.filename#1{\_ea\.filenameA#1/\.end} -\_def\.filenameA#1/#2{\_ifx\.end#2#1\_else\_afterfi{\.filenameA#2}\_fi} - -\_def\.fileext#1{\_ea\.fileextA#1.\.end} -\_def\.fileextA#1.#2{\_ifx\.end#2#1\_else\_afterfi{\.fileextA#2}\_fi} - - \_doc - MIME type is determined from file extension (e.g. `mp4` is \"video/mp4"). For - mapping of file extensions to MIME types we abuse \TeX/'s hash table which - gets populated with \"known MIME types". This necessarily means that the - database is incomplete. Users can define their own additional mappings, or - they can contribute generally useful ones to this package. - - The default MIME type (used for unknown file extensions) is - \"application/octet-stream"~-- binary data. - - The uninteresting MIME type database itself is at the very end (\ref[mime]). - \_cod - -\_def\.mimetoname[#1/#2]{/#1\_csstring\#2F#2} - -\_def\.defaultmimetype{application/octet-stream} -\_def\.exttomime#1{\.trycs{mimetype:#1}{}} - - \_doc - Here we define an \OpTeX/ style \"is-macro" that checks whether the file has - already been defined~-- \`\.isfiledefined``{<name>}\iftrue` (or `\iffalse`). - The case where the file has not - been defined using `\filedef` can be handled in a lot of ways. As a default - we interpret <name> as path and try to embed it. Because the path from <name> - is used as the \"friendly name" the file will be embedded only once even when - requested more times. - \_cod - -\_def\.isfiledefined#1#2{\.isdefined{filespec:#1}\_iftrue\_else - \_afterfi{\.fileundefined{#1}}\_fi#2% -} - -\_def\.fileundefined#1{\_isfile{#1}\_iftrue\.filedef/e[#1]{#1}\_else - \_opwarning{file '#1' not found, ignored}\_ea\_unless\_fi -} - -% strict requirement of preceeding `\filedef` can be set like this: -%\_def\.fileundefined#1{\_opwarning{file '#1' is not defined, ignored}\_unless} - - \_doc - \label[actions] - \sec PDF actions - - The core of interactivity in PDF are actions. They are all initialy handled - by \`\pdfaction``[<action spec>]`. <action spec> is a comma separated list of - `<type>:<arguments>`. Leading spaces in the elements of the list are ignored - using undelimited-delimited argument pair trick. - - An invocation could look like this: - - \begtt - \pdfaction[ - js:{app.alert("Yay JavaScript, going to page 5");}, - ilink:pg:5, - transition:Wipe, - ] - \endtt - - This is why we have to be very careful when loading the contents between `[]` - to arguments. In particular, we can't split immediatly using `[#1:#2]`, - because this would discard the braces guarding the comma in the JavaScript - code. However we also need to find out the {\em type} of action which is - taken as a type of the first action (`js` in this case). - \`\.pdfactiontype``[<action spec>]` does this~-- we don't mind that there the - braces are lost. - - `\pdfaction` processes the list, to create a chain of actions using `/Next` - field. The handling of each action type is up to macro - `\_pdfextra_<type>action`, which receives `[<type>:<arguments>]`. Because of - this a single type handler can handle multiple different actions, as is the - case with `\.ilinkaction` which is the fallback for unknown action types. - \_cod - -\_def\.pdfaction[#1#2]{\.pdfactionA#1#2,\.stop\.end} -\_def\.pdfactionA#1,#2#3\.end{% - <<% - \.pdfactionB[#1]% - % next action - \_ifx\.stop#3\_else\_space - /Next \_afterfi{\.pdfactionA#2#3\.end} % intentional space - \_fi - >> -} -\_def\.pdfactionB[#1:#2]{\.trycs{#1action}{\_ea\.ilinkaction}[#1:#2]} -\_nspublic \pdfaction ; - -\_def\.pdfactiontype[#1:#2]{#1} - - \_doc - \label[actions-additional] - \secc Additional actions - Some PDF objects, like pages and some annotations, can also have \"additional - actions". These are actions which will be executed when an event happens~-- - like page getting opened for `/O` action in page's additonal actions or `/PO` - in annotation's additional actions. For constructing these additional actions - we define a helper macro \`\.pdfaactions`. The use is as something follows: - \begtt \catcode`<=13 \adef|{\csstring<} - /AA || \.pdfaactions{ {O} {<action spec 1>} {C} {<action spec 2>} } >> - \endtt - To produce something this: - \begtt \catcode`<=13 \adef|{\csstring<} - /AA || /O ||<action 1>>> /C ||<action 2>>> >> - \endtt - \_cod - -\_def\.pdfaactions#1{<<\.pdfaactionsA #1\.end\.end>>} -\_def\.pdfaactionsA#1#2{\_ifx\.end#1\_else /#1 \_ea\.pdfaction\_ea[#2]\_ea\.pdfaactionsA\_fi} - - \_doc - \label[actions-link] - \secc Link annotations - The main use of actions~-- annotations of `/Subtype /Link`. Annotation of - this type creates an active rectangular area on the page that executes a PDF - action (or chain of them in the general case). - \`\hlink``[<action spec>]<text>` is macro that typesets <text> and makes area - occupied by it active according to <action spec>. All action types are - supported, the mechanism is completely generic. - - The `\pdfstartlink`/`\pdfendlink` primitives are used to denote the part of - the page where <text> appears as active. \LuaTeX/ will then handle even the - situations where <text> gets broken across multiple lines (by creating - multiple rectangular annotations to cover all `\hbox`es). - \_cod - -\_def\.hlink[#1]#2{\_bgroup\_def\#{\_csstring\#}% - \_edef\.type{\.pdfactiontype[#1]}% - \_quitvmode\_pdfstartlink \.linkdimens - attr{\_pdfborder{\.type}}% - user{/Subtype /Link /A \.pdfaction[#1]}\_relax - \_localcolor\.linkcolor{\.type}#2\_pdfendlink\_egroup -} - -\_nspublic \hlink ; - - \_doc - Use `\hlink` as the backing command for OpTeX's \"higher level" linking - commands (`\ilink` and `\ulink`). - - The lower level ones (`\xlink` and its predecessor `\link` actually have - completely different semantics with regards to color, so we keep them as they - are. - \_cod - -\_protected\_def\_ilink[#1]#2{\.hlink[#1]{#2}} -\_protected\_def\_ulink[#1]#2{{\_escapechar=-1 \_ea}\_expanded - {\_noexpand\.hlink[url:\_detokenize{#1}]}{#2}} - -\_public \link \ilink \ulink ; - -%\_protected\_def\_link[#1]#2#3{\_hlink[#1]{#3}} -%\_protected\_def\_xlink#1#2#3#4{\_hlink[#1:#2]{#4}} - - \_doc - Two customizations of `\hlinks` are possible: - \begitems - * Dimensions of rectangular areas created by - `\pdfstartlink`/`\pdfendlink`. This is done using \`\.linkdimens` - (analogous to \OpTeX's `\linkdimens`). Dimensions that are unset are taken - from the respective `\hbox`es. \`\lininglinks` sets the dimensions for - running text~-- it covers all space of a line using `\baselineskip`. - \`\nolininglinks` sets no dimensions, this is useful for buttons, that may - have larger height/depth than a line. - * The color is determined from the type of link (that is, the first action in - <action spec>) by checking `\_<type>linkcolor` (compatible with \OpTeX/). - As a fallback `\_ilinkcolor` is used (set by \OpTeX's `\hyperlinks`) for all links - except for URLs, where `\_elinkcolor` is used instead. If even these - fallback colors are not defined (`\hyperlinks` isn't used), then the most - generic `\_linkcolor` will be taken or no color will be set. - \enditems - \_cod - -\_def\.lininglinks{% - \_def\.linkdimens{height.75\_baselineskip depth.25\_baselineskip}% -} -\_def\.nolininglinks{\_def\.linkdimens{}} -\.lininglinks - -\_nspublic \lininglinks \nolininglinks ; - -\_def\.linkcolor#1{\_trycs{_#1linkcolor}{\_trycs{_ilinkcolor}{\_trycs{_linkcolor}{}}}} - - % \_urllinkcolor = \_elinkcolor with fallbacks -\_def\_urllinkcolor{\.linkcolor{e}} - - \_doc - \secc Open action - The document itself has one action defined in the document catalog. It is - called `/OpenAction`. We allow the user to set it using the familiar - <action spec> syntax with the command \`\openaction``[<action spec>]`. - - Internally we could directly set it by appending to the catalog - using the primitive `\pdfcatalog`, but \LuaTeX/ (pdf\TeX/ really) allows - setting the action with special syntax. This has the benefit that it is not - allowed to set the action more than once. - \_cod - -\_def\.openaction[#1]{\_pdfcatalog{} openaction user{\.pdfaction[#1]}\_relax} -\_nspublic \openaction ; - - \_doc - \label[actions-jump] - \secc Jump actions - - These are the most typical actions. Even \LuaTeX/ itself handles them, - although we don't use the possibility for maintaining generality. There are a - few types of jump actions: - \begitems - * `/GoTo` actions are the classic internal links to named destinations in the - PDF file (created by `\pdfdest` primitive or \OpTeX/'s `\dest`). The - destination names include also the type of internal link (e.g. - `ref:section1`). They are handled by \`\.ilinkaction``[<type>:<name>]`. - * `/URI` actions which are in most cases used as \"goto URL" actions. These - are not that useful directly, because special characters should be handled - before this actions is used (like with `\url`). The low level use is - \`\.urlaction``[url:<url>]`. - * \"Goto remote" actions, which can jump to a destination in another PDF file~-- - either determined by name, or by page number. The external files are - expected to be defined by `\filedef` (but not the embedded variant). The - use is either \`\.extrefaction``[extref:<name>:<named destination>]` for - links to named destination or \`\.extpgrefaction``[extpgref:<name>:<page number>]` - for page destinations. Customization is possible with \`\.extrefextra`, by - default opening in a new windows is requested. - \enditems - \_cod - -\_def\.ilinkaction[#1:#2]{/S /GoTo /D (#1:#2)} - -\_def\.urlaction[#1:#2]{/S /URI /URI (#2)} - -\_def\.extrefaction[#1:#2:#3]{/S /GoToR - /F \.cs{filespec:#2} - /D (#3) - \.extrefextra -} -\_def\.extpgrefaction[#1:#2:#3]{/S /GoToR - /F \.cs{filespec:#2} - /D [\_the\_numexpr#3-1\_relax\_space /Fit] - \.extrefextra -} - -\_def\.extrefextra{/NewWindow true} - - - \_doc - Transition action is not really a jump action in of itself, but is only - useful when chained after jump actions, so we define it here. Transitions - (as page attributes) are handled more thoroughly in section~\ref[transitions]. - - The use would look something like:\nl - \`\.transitionaction`% - `[transition:<animation type>:<duration>:<raw PDF attributes>]`, where all fields - omitted from right take the default values. - \_cod - -\_def\.transitionaction[#1:#2]{/S /Trans \.attrorempty{Trans}{\.maketrans[#2]}} - - \_doc - \secc Named actions - - User can request arbitrary \"named" action with - \`\.namedaction``[named:<name>]`. See user documentation for details. - \_cod - -\_def\.namedaction[#1:#2]{/S /Named /N /#2} - - \_doc - \secc JavaScript actions - - JavaScript actions have two forms, either \`\.jsaction``[js:<name>]` or - `\.jsaction``[js:<script>]`. The first variant uses contents of `\filedef`'d - <name>, the second one uses <script> directly. There is no special catcode - handling. - - \_cod - -\_def\.jsaction[#1:#2]{/S /JavaScript - /JS \_ifcsname _pdfextra_filestream:#2\_endcsname \_lastnamedcs \_else - (#2) - \_fi -} - - \_doc - \sec Page attributes - - PDF represents pages as dictionaries. The dictionaries get generated by \LuaTeX/, - which fills in some attributes {\em attributes} (like `/Content` with - contents of the page and `/Annots` with array of annotations). We can add - more using `\pdfpageattr` primitive token list register. While not that many - are generally useful, there are a few interesting ones. For example - transitions can be set using page attributes, or we might want to set - additional actions (`/AA`) to listen for page events. - - While the so called \"page objects" are in a tree structure (for fast lookup), - only the leaves are real \"pages". PDF allows some attributes to be inherited - from parent page objects, but not all of them and certainly not those we are - interested in. - - The mechanism introduced in this section is optional, because it takes - complete control over `\pdfpageattr`. It gets activated when - \`\initpageattributes` is first used (which happens automatically for some - functionality exposed by this package), but may be activated by the user for - any other purpose. Only attributes listed in \`\pageattributes` are - processed. - - We set the attributes anew for each page, by hooking into \OpTeX/'s - `\_begoutput`. Because `\pdfpageattr` token list doesn't get expanded before - written out to PDF, we expand it using the assignment in `\edef` trick. The - token list gets expanded, but the assignment is not made until it reaches - main processor when the temporary control sequence gets expanded. - \_cod - -% pdfpagattr managament (default for all pages vs current page override) -\_def\.pageattributes{{Trans}{Dur}{Rotate}{AA}} -\_def\.initpageattributes{% - % add hook for setting primitive \pdfpageattr - \_addto\_begoutput{\_edef\.tmp{\_pdfpageattr={\.pdfpageattributes}}\.tmp}% - % no need to do this twice - \_let\.initpageattributes=\_relax -} -\_nspublic \pageattributes \initpageattributes ; - - \_doc - The user interface we want to expose has two parts: - \begitems - * setting the page attribute for just this one page (\`\.pdfcurrentpageattr`), - * setting the default attribute (used when current page value is not set) - (\`\.pdfdefaultpageattr`). - \enditems - - The first one of course brings in the typical \TeX/ problem of knowing the - page where something occurs. As always, the page number contained in - `\gpageno` during processing of said content may of course not actually be - the number of the page where the content ends up! Hence, we need to note the - page number with a delayed write, using `\.setpageof` and later `\.pageof`. - The different settings of page attributes should have distinct names, we use - the \`\.pageattrcount` counter for this. - \_cod - -\_newcount\.pageattrcount -\_def\.pdfcurrentpageattr#1#2{\.initpageattributes - \_incr\.pageattrcount - \.setpageof{pageattr:\_the\.pageattrcount}% - \.sxdef{pdfpgattr:\.pageof{pageattr:\_the\.pageattrcount}:#1}{#2}% -} -\_def\.pdfdefaultpageattr#1#2{\.initpageattributes - \.sxdef{pdfpgattr:#1}{#2}% -} - - \_doc - Finally, the macro \`\pdfpageattributes` takes care of setting generating the - contents of `\pdfpageattr`. For each attribute in `\pageattributes` it first - checks its current page value, only then the default value. If neither is - set, nothing is added. - \_cod - -\_def\.pdfpageattributes{\_ea\.pdfpageattributesA\.pageattributes\.end} -\_def\.pdfpageattributesA#1{\_ifx\.end#1\_else - % use current page override or "default" - % don't emit anything if the value is empty - \.attrorempty{#1}{% - \.trycs{pdfpgattr:\_the\_gpageno:#1}{\.trycs{pdfpgattr:#1}{}}% - }% - \_ea\.pdfpageattributesA\_fi -} - - \_doc - Each attributes then has two switches for the respective default and current - values. For defining a few of them a helper is introduced:\nl\indent - \`\.pdfpageattributesetters`` <attribute> \<default setter> \<current setter> {<value>}`, - \nl - where <attribute> is name of the attribute without the slash - (e.g. `MediaBox`), the two control sequences name the future user setters, - which will take single argument in brackets - (e.g. `\mediabox` and `\thismediabox`) and the <value> can use the - argument. - \_cod - -\_def\.pdfpageattributesetters#1 #2#3#4{% - \.sdef{\_csstring#2}[##1]{\.pdfdefaultpageattr{#1}{#4}}% - \.sdef{\_csstring#3}[##1]{\.pdfcurrentpageattr{#1}{#4}}% - \_nspublic #2 #3 ; -} - - \_doc - Some of the useful attributes are `/Rotate`, which rotates the pages visually - (can be set with \`\defaultpagerotate` and \`\pagerotate`), and the additional - actions (`/AA`, see section~\ref[actions-additional], set using - \`\defaultpageactions` \`\pageactions`). - \_cod - -\.pdfpageattributesetters Rotate \defaultpagerotate \pagerotate {#1} - -\.pdfpageattributesetters AA \defaultpageactions \pageactions {\.pdfaactions{#1}} - - \_doc - \label[transitions] - \secc Transitions, page durations - - There are predefined types of transitions, like `/Wipe`, `/Box`, `/Split`, - etc. Most have other customizible attributes~-- usually directions set in - different ways depending on the animation type at hand, but the most - important attribute is the duration of the animation. Parsing friendly user - notation in the form of - `[<animation type>:<duration>:<other raw attributes>]`, where fields from the - right may be omitted to produce the default value, is handled by - \`\.maketrans`. This macro is also used by transition actions - (see~\ref[actions-jump]). The defaults are simply those defined by PDF - standard (no transition, 1 second duration and the respective default - directions). - \_cod - -\_def\.maketrans[#1]{\.maketransA#1:::\.end} -\_def\.maketransA#1:#2:#3:#4\.end{% - \.emptyor{#1}{<</S /\.nonempty \.attrorempty{D}{#2} #3>>} -} - - \_doc - The attribute setters for transitions (\`\transitions`, \`\transition`) are a - simple wrappers. Similiar is the - setting of page duration in seconds after which PDF viewer automatically - advances to the next page (\`\defaultpageduration`, \`\pageduration`). - \_cod - -\.pdfpageattributesetters Trans \transitions \transition {\.maketrans[#1]} - -\.pdfpageattributesetters Dur \defaultpageduration \pageduration {#1} - - \_doc - \label[nametrees] - \sec Name trees -- attachments and document level JavaScript - - These don't have any last place to be in, so they are documented separately, - here. Attaching files using `/FileAttachment` annotations: - \begitems \style n - * is intended more towards viewers of the document for extra additions and - * doesn't work in the viewers as well as one would like. - \enditems - - That is why instead embed files using normal `\filedef` and then allow them - to be added to the document level `/EmbeddedFiles` entry, which means they - will be shown in the user interface by PDF viewers. `/EmbeddedFiles` is a - document level name tree (contained inside `/Names` entry of `/Catalog`) that - maps names of files to their objects. Although we simplify matters by - constructing more of an array. - - What works very similiarly is document level JavaScript. It is a name tree - within `/JavaScript` field. It maps names of JavaScript actions to their - object numbers. The names aren't very useful, but the actions have their - purpose. They are executed in turn after the document is opened. Hence they - can be used to predefine JavaScript functions in the global context, to be - used later within actions explicitly activated by the user. - - The user level commands are \`\attach``[<name>]` (to attach a previously - `\filedef`'d name with fallback to embedding now if it is a valid path) and - \`\dljavascript``[<name>]{<script>}` (adds action that executes <script> - after document is opened, <name> is more or less meaningless). - - Internally the commands construct lists of what ends up in the resulting name - array, i.e. pairs {\visiblesp `(<name>) <object number> 0 R `}. Intermediate - macros \`\.embeddedfiles` and \`\.dljavascripts` are used for this. - - In the case of file attachments, nothing happens if file is defined and not - found by the fallback. - - \_cod - -% file attachment -\_def\.embeddedfiles{} -\_def\.attach[#1]{\.isfiledefined{\.name}\_iftrue - \.xaddto\.embeddedfiles{(#1) \.cs{filespec:#1} }\_fi -} -\_nspublic \attach ; - -\_def\.dljavascripts{} -\_def\.dljavascript[#1]#2{% - \_immediate\_pdfobj{<< \.jsaction[js:{#2}] >>}% - \.xaddto\.dljavascripts{(#1) \_the\_pdflastobj \_space 0 R }% -} -\_nspublic \dljavascript ; - - \_doc - Renditions (see \ref[mm-renditions]) also need their name tree. This package - mostly doesn't play well with Unicode filenames, that is why they are - forbidden. However, Renditions that are accessed from JavaScript have to be - named/present in a `/Renditions` name tree, with the names encoded in the PDF - encoding (UTF-16BE).\nl - The names and object references are collected in \`\.renditions`. Unicode - encoding is hacked with \`\.pdfstringtounicode`. - \_cod - -\_def\.pdfstringstrip(#1){#1}% -\_def\.pdfstringtounicode#1#2{% - \_ea\_pdfunidef\_ea#1\_ea{\_ea\.pdfstringstrip\_expanded{\.cs{filename:#2}}}% -} -\_def\.renditions{} - - \_doc - Object creation, which is common to all name trees, is handled by\nl\indent - \`\.makenamearray``<name tree name><name tree content>`.\nl - It doesn't do anything - for empty lists, to not bloat PDF files when this mechanism isn't used. - \_cod - -\_def\.makenamearray#1#2{\_ifx#2\_empty\_else - \_immediate\_pdfobj {<< /Names [ #2 ] >>}% - \_pdfnames{/#1 \_the\_pdflastobj \_space 0 R }\_fi -} - - \_doc - The lists themselves can only be written out to the PDF file at the very end - of the run. We use \OpTeX/'s `\_byehook`, which is run in `\_bye`. But `\bye` - itself may be predefined by the user, for example when using some of the - \OpTeX/ tricks. We just hope that the user keeps `\_byehook`. - \_cod - -\_addto\_byehook{% - \.makenamearray{EmbeddedFiles}\.embeddedfiles - \.makenamearray{JavaScript}\.dljavascripts - \.makenamearray{Renditions}\.renditions -} - - \_doc - \sec Viewer preferences - - There are a few customizations of display (and other preferences of PDF - viewers) possible in the document catalog or its subdictionary - `/ViewerPreferences`. Most are not that useful. The interesting - ones are implemented by \`\fullscreen`, \`\showoutlines`, \`\showattached`. - They all set the page mode using \`\.setpagemode`. We don't handle respecting - the last setting (using \`\_byehook`). To prevent invalid PDF files, we set - `\.setpagemode` to `\_relax` after use. - \_cod - -\_def\.setpagemode#1{\_pdfcatalog{/PageMode /#1}\_glet\.setpagemode=\_relax} - -\_def\.fullscreen{\.setpagemode{FullScreen}} -\_def\.showoutlines{\.setpagemode{UseOutlines}} -\_def\.showattached{\.setpagemode{UseAttachments}} - -\_nspublic \fullscreen \showoutlines \showattached ; - - \_doc - Only the setting of duplex / double sided printing and display is in the - nested dictionary. It is handled by \`\duplexdisplay`. The simplistic version - does not handle more attributes in `/ViewerPreferences`. We also set the - meaning to `\_relax` to prevent more (erroneous) uses. - \_cod - -\_def\.duplexdisplay{\_pdfcatalog{% - /PageLayout /TwoPageRight - /ViewerPreferences << - /Duplex /DuplexFlipLongEdge - >>}% - \_glet\.duplexdisplay=\_relax -} - -\_def\duplexdisplay{\.duplexdisplay} - - \_doc - \sec Multimedia - - PDF essentially allows insertion of different types of multimedia: - \begitems - * images, - * audio/video, - * 3D art. - \enditems - - The first is pretty standard and handled normally by the engine (\LuaTeX). - Others are possible, but have to be done manually according to one of the - mechanisms specified by PDF standard: - - \begitems - * Sounds (audio only), - * Movies (video and/or audio), - * Renditions (video and/or audio), - * 3D annotations (3D art), - * Rich Media (video and/or audio, 3D art) - \enditems - - Sadly all these mechanisms are badly flawed, each in different ways. At least - we try to use the one that works in the viewers. - - For audio/video \"Movies" are the simplest mechanism, but they have been - deprecated in PDF 2.0 and no longer work in Acrobat/Foxit (same for - \"Sounds"). - - \"Renditions" are complicated, partly dependant on JavaScript, but at least - supported by Acrobat, Foxit, Evince and Okular. - - \"Rich media" annotations were designed for Flash. This use case is no longer possible - today, but the obscurities remain. They are unnecessiraly complicated, but can - be used without Flash too. Although the result is very plain for audio/video~-- - no controls can be displayed and there are no associated actions. - - \"3D annotations" are reasonably simple, but also flawed. They cannot reuse - embedded file as a source for 3D data. Hence it is better and more consistent - to use Rich Media for 3D annotations. It even has additional benefits, like - the possibility of using multiple initialization scripts. - - In the end, this package exposes two user commands corresponding to two - mechanisms~-- first are Renditions (`\render`) for audio/video that works in most - browsers and Rich Media (`\RM`) mainly for 3D art, but also for audio/video - with limited possibilities. - - Both mechanisms have an annotation at their core. Annotations is essentially - a rectangular area on page. The area corresponds to where the - multimedium will show up. After activating the area somehow (by user click, - or action) the multimedium will start playing. Before annotations the - rectangular area will show something that is called \"normal appearance". - This appearance is of type form XObject. Those are really similiar to pages~-- - they have dimensions, contents made up of PDF graphics operators, \dots, - but they are reusable. Not that useful for annotations where we will need the - form only once, but nice anyways. pdf\TeX/ has primitives for creating them~-- - `\pdfxform` and friends. They essentially do the same code like `\shipout` - does, but instead of page, they make this reusable object. One can then - either use this reusable object in another page/form, but we will indirectly - refer to it for the appearance. - - Important aspect of annotations is that they are really only rectangular - areas on the page, but they are not really part of the page. They sort of sit - on another level and are not influenced by PDF graphic operators which make - the page. In pdf\TeX/ annotations are handled by {\em whatsit} nodes. While - most nodes map to known primitive \TeX/ concepts (like typeset characters, - boxes, rules, etc.) Whatsits are essentially commands for \TeX/ that are - delayed until page is being shipped out (written to PDF file). `\write`, - `\special`, and most pdf\TeX/ commands create whatsits. For annotationos this - is important, because this means that the engine only stores the information - about annotation that we specify, but creates it at due time, when it should - be written to PDF. - - Because whatsits are essentially dimensionless and we want it to be a part of - normal \TeX/ typesetting material we create the annotation (whatsit) in - `\hbox`. This box will be otherwise empty, because the apperance of the - rectangular area is determined by the normal appearance field (`/N` in - `/AP`). We set the dimensions of the box to the dimensions of normal - appearance. Everything will line up nicely, because when processed, the - annotation will take dimensions from the box. - - All of these concepts are implemented in:\nl\indent - \`\.boxedannot``[<type>:<name>]{<appearance>}{<special text>}{<annotation attributes>}`\nl - <type> is used to determine the annotation border (same principle as with - Link annotations, section~\ref[actions-link]), <name> will be used as the - annotation name (`/NM`), <special text> is used for influencing the - `\pdfannot` primitive, and <annotation attributes> will become the body of - the annotation. - - \_cod - -\_def\.boxedannot[#1:#2]#3#4#5{% - \_setbox0=\_hbox{#3}\_setbox2=\_null - \_ht2=\_ht0 \_wd2=\_wd0 \_dp2=\_dp0 - \_preshipout0 \box0 - \_immediate\_pdfxform0 - % box with annotation both stretching to dimensions of appearance - \_hbox{\.setpageof{#1:#2}% - \_pdfannot #4 {#5 - /AP <</N \_the\_pdflastxform \_space 0 R>> - \_pdfborder{#1} - /NM (#2) - /Contents (#1 '#2') - }% - \_copy2 - }% -} - - \_doc - There is another weird thing common to both multimedia mechanisms~-- - the redefinition of `\.name`. It is initially - set by `\.secondoptdef` to <name>, but may be redefined by user supplied - `name` key-value parameter. This should be used when there are multiple uses - of the same content. Otherwise samely named annotations would be - indistinguishable both for PDF viewer and our handling of actions (which - would all refer only to the first instance). - - To somewhat overcome this, trying to use the same <name> (within the same - type of annotaiton) will use dummy name from \`\.unnamedannotcount` (for - uniqueness). This means that <name> will always refer to the first instance. - \`\.redefinename` handles this. - \_cod - -\_newcount\.unnamedannotcount -\_def\.redefinename#1{% - \.isdefined{#1:\.name}\_iftrue - \_incr\.unnamedannotcount - \_edef\.name{\_the\.unnamedannotcount}% - \_else - \_edef\.name{\_kv{name}}% - \_fi -} - - \_doc - \label[mm-renditions] - \secc Renditions (audio/video) - - There are three main types of PDF objects involved in the Renditions - (\"Multimedia") mechanism: - - \begitems - * Screen annotations define the area for playing multimedia. - * Rendition objects define the multimedia to play. - * Rendition actions associate Rendition objects with Screen annotations. - \enditems - - You can theoretically arbitrarily mix and match rendition objects and screen - annotations by invoking different actions. In practice Evince and Okular do - really simplistic parsing and don't fully support the actions fully. But by - keeping it simple it is possible to make it work almost the same in all - viewers that support renditions. - - Different sources of audio/video should be possible. In fact all three file - specifications (embedded files, files specified by URL/path) could work. - Again in practice embedded file is the safest bet, that works in all viewers - that support renditions. - - The user facing command is:\nl\indent - \`\render``[<name>][<optional key-value paramers>]{<horizontal material>}`\nl\noindent - <name> is the friendly name set using `\filedef` or file path if <name> isn't - `\filedef`d and is to be embedded. The key-value parameters in brackets - can be entirely omitted. They can influence the playback (except for `controls` - most are not well supported). Default values are taken from - `\.renderdefaults`. - - `\render` doesn't do anything (except print warning) if file <name> isn't - defined and <name> isn't a path to file that can be embedded. - - The first PDF object it defines is Rendition, which specifies information - about the multimedium (name, file specification, MIME type and options from - key-value parameters). Some of the fields are in `/BE` (\"best effort") - dictionaries. This is due to the very general design of Renditions, which - theoretically allows the PDF viewer to choose from multiple Renditions if - they know they can't support some of the requested features. But that is not - much useful in practice, so we just don't complicate it. - - Next defined object is Screen annotation, which complicates thing by - requiring (`/P`) reference to the page where the annotation is (handled by - `\setpageof` and `\pageof` pair). Important field is `/A` which specifies - actions that shall be executed when the screen area is clicked. We let the - user change the action, but the sensible default of starting to play the - multimedium is used (and this is the only thing that works in some viewers - anyways). Additional actions `/AA` may be used to react to events like mouse - over or page open/close~-- the most probable use case is autoplay on page - open, for which shortcut of \`\renditionautoplay` is defined. - - The code is slightly complicated by the fact, that actions need to reference - the Rendition and Screen objects. In the case of the action contained in - Screen annotation this essentialy involves a self reference. Hence it is - needed to first reserve an object number and later use it for the annotation. - Because the object numbers may also be needed by actions defined later, we need - to save them to `\_pdfextra_rendition:<name>` and `\_pdfextra_screen:<name>` - respectively, but also define aliases with empty names, so users can easily - reference the latest rendition. - - \_cod - -\.secondoptdef\.render#1{\.isfiledefined{\.name}\_iftrue\_bgroup - \_ea\_readkv\_ea{\_ea\.renderdefaults\_ea,\_the\_opt}% - \.colortorgbdef\.bgcolor{\_kv{background}}% - % rendition object ("media specifaction") - \.pdfstringtounicode\.uiname\.name - \_pdfobj {<</Type /Rendition - /S /MR - /N \.uiname - /C <<%/Type /MediaClip - /S /MCD % subtype MediaClipData - /D \.cs{filespec:\.name} - /CT (\.cs{filemime:\.name}) - /P << /TF (TEMPALWAYS) >> % allow creating temporary files - >> - /P <<%/Type /MediaPlayParams - /BE << /C \_kv{controls} /V \_kv{volume} /RC \_kv{repeat} >> - >> - /SP <<%/Type /MediaScreenParams - /BE << /O \_kv{opacity} /B [\.bgcolor] >> - >> - >>}\_pdfrefobj\_pdflastobj - \.xaddto\.renditions{\.uiname \_the\_pdflastobj \_space 0 R }% - \.redefinename{rendition}% - \.sxdef{rendition:\.name}{\_the\_pdflastobj}% - % screen annotation ("screen space allocation") - \_pdfannot reserveobjnum% "self" reference will be needed inside screen annot. - \.sxdef{screen:\.name}{\_the\_pdflastannot}% - % aliases to latest rendition/screen with empty name - \_global\.slet{rendition:}{rendition:\.name}% - \_global\.slet{screen:}{screen:\.name}% - \_edef\.action{\_kv{action}}\_edef\.aactions{\_kv{aactions}}% - \.boxedannot[rendition:\.name]{#1}{useobjnum\_the\_pdflastannot}{% - /Subtype /Screen - % reference to page of the rendition (\setpageof done by \.boxedannot) - % the spaces are weird, but \pdfpageref eats them - /P \_pdfpageref\.pageof{rendition:\.name} \_space 0 R - /A \_ea\.pdfaction\_ea[\.action] - /AA \_ea\.pdfaactions\_ea{\.aactions} - }% - \_egroup\_fi -} -\_nspublic \render ; - - \_doc - Here are the defaults used for `\render`~-- \`\.renderdefaults`. Users can - redefine them all together or override as needed with key-value parameters. - The defaults correspond to values specified by PDF standard. Other values may - not be respected by all viewers. - \_cod - -\_def\.renderdefaults{% - name=\.name, - controls=false, - volume=100, - repeat=1, - opacity=1.0, - background=1 1 1, - action=rendition::play, - aactions={}, -} - - \_doc - Most probable use of additional actions is to start auto-start playing of the - multimedium. For this purpose \`\renditionautoplay` is defined as a shorthand - for action to play the lastly defined rendition on page visible event. - \_cod - -\_def\.renditionautoplay{{PV}{rendition::play}} -\_nspublic \renditionautoplay ; - - \_doc - \seccc Rendition actions - - Rendition actions unfortunately use cryptic symbolic numbers (`0`, `1`, `2` - and `3`) for actions that could be called `play`, `stop`, `pause` and - `resume` respectively. Except for these predefined actions (that use `/OP`) - running of JavaScript is possible using `/JS (<script>)` with potential - fallback to `/OP`. This is dangerous teritory, because support of the right - API in the viewer is very low. Although it is possible to define such action - type by: - - \begtt -\.sdef{renditionaction:myaction}{/JS (app.alert("something useful");) /OP 0} - \endtt - - The use of rendition action is: - \`\.renditionaction``[rendition:<name>:<action type>]`. Empty name refers to - last rendition, so e.g.`\.renditionaction``[rendition::pause]` is possible. - \_cod - -\.sdef{renditionaction:play}{/OP 0} -\.sdef{renditionaction:stop}{/OP 1} -\.sdef{renditionaction:pause}{/OP 2} -\.sdef{renditionaction:resume}{/OP 3} -\_def\.renditionaction[#1:#2:#3]{/S /Rendition - \.cs{renditionaction:#3} - /R \.cs{rendition:#2} 0 R - /AN \.cs{screen:#2} 0 R% -} - - \_doc - \secc Rich Media (3D/audio/video) - - Some principles seen with Renditions (section~\ref[mm-renditions]) apply here - too. But additionally we deal with 3D specifics and unfortunate Flash - leftovers. - - Unlike Renditions both page area and multimedium specifaction are handled in - a single annotation~-- the Rich Media annotation. The code is unfortunately - obscured due to the weird requirements, but this is essentially what we are - trying to create with \`\RM`: -\begtt -/Type /Annot -/Subtype /RichMedia -/RichMediaSettings << - /Activation << - /Condition /PV - /Scripts [ 14 0 R ] - >> - /Deactivation << /Condition /XD >> ->> -/RichMediaContent << - /Assets << /Names [ (kladka.prc) 2 0 R (wireframe.js) 14 0 R ] >> - /Configurations [ << - /Type /RichMediaConfiguration - /Subtype /3D - /Instances [ << - /Type /RichMediaInstance - /Subtype /3D - /Asset 2 0 R - >> ] - >> ] ->> -\endtt - - The activation/deactivation will be dealt with later. But we see that to - insert a simple 3D file, we have to pack it inside a file specification - (indirect reference to object `2 0 R`), then in \"instance", inside a - \"configuration" inside \"content". As if it wasn't enough the names - (normally contained in the file specification) have to be specified again in - `Assets` name tree that uselessly maps names to file specifications. Because - this is a 3D Rich Media annotation there are other files at play~-- - initialization scripts. These are specified in `/Scripts` and are executed in - turn when the annotation is activated. Not shown is, that some - \"configurations" and \"instances" actually have to be specified indirectly. - - If it wasn't for Flash we could do with something like: - -\begtt -/Type /Annot -/Subtype /RichMedia -/Activation /PV -/Scripts [ 14 0 R ] -/Deactivation /XD -/Content 2 0 R -\endtt - - Which contains equivalent information. But unfortunately here we are\dots - \_cod - -\.secondoptdef\.RM#1{\.isfiledefined{\.name}\_iftrue - \_edef\.tmp{\.cs{filemime:\.name}}% - \_edef\.subtype{\_ea\.mimetormsubtype\_ea[\.tmp]\_space}% - \_ifx\.subtype\_space - \_opwarning{unknown rich media type for '\.name', ignored}\_else - \_bgroup - \_ea\_readkv\_ea{\_ea\.RMdefaults\_ea,\_the\_opt}% - % Instance that has the media file as an asset - \_pdfobj {<</Type /RichMediaInstance - /Subtype /\.subtype - /Asset \.cs{filespec:\.name} - >>}\_pdfrefobj\_pdflastobj - % Configuration with one single instance (the above) - \_pdfobj {<</Type /RichMediaConfiguration - /Subtype /\.subtype - /Instances [ \_the\_pdflastobj \_space 0 R ] - >>}\_pdfrefobj\_pdflastobj \_edef\.configuration{\_the\_pdflastobj}% - \_edef\.names{\.cs{filename:\.name} \.cs{filespec:\.name} }% initial asset - \.redefinename{rm}% - \_def\.scriptfilespecs{}% - \_edef\.views{\_kv{views}}\_edef\.scripts{\_kv{scripts}}% - \_ifx\.views\_empty \_edef\.views{\.name}\_fi - \_ea\.DDDscripts\_ea{\.scripts}% - % annotation in hbox - \.boxedannot[rm:\.name]{#1}{}{% - /Subtype /RichMedia - /RichMediaSettings << - /Activation << - /Condition \.cs{activation:\_kv{activation}} - \.emptyor{\.scriptfilespecs}{/Scripts [ \.nonempty ]} - /Presentation << /Toolbar \_kv{toolbar} \.RMpresentationextra >> - >> - /Deactivation << /Condition \.cs{deactivation:\_kv{deactivation}} >> - >> - /RichMediaContent << - /Assets << /Names [ \.names ] >> - /Configurations [ \.configuration \_space 0 R ] - \.emptyor{\_ea\.DDDviews\_ea{\.views}}{/Views [ \.nonempty ]} - >> - }% - \.sxdef{rm:\.name}{\_the\_pdflastannot}% - \_global\.slet{rm:}{rm:\.name}% - \_egroup\_fi\_fi -} -\_nspublic \RM ; - - \_doc - The code is similiar to `\render`, but we also ignore everything if we don't - recognize the type of media (`Video`, `Sound` or `3D`). For that we use a - simple mapping from MIME types with \`\.mimetormsubtype`. This means that - although we aim Rich Media mostly for 3D art it may also be used for Video - and Sound. - \_cod - -\_def\.mimetormsubtype[#1/#2]{\.cs{rmtype:#1}} - -\.sdef{rmtype:model}{3D} -\.sdef{rmtype:video}{Video} -\.sdef{rmtype:audio}{Sound} - - \_doc - Then we also need to construct the weird name \"tree" (essentialy an array in - our case) and script array. \`\.DDDscripts` and \`\.DDDviews` do this. - Name tree is accumulated in `\.names`, and starts with the media file. After - that each script is added to \`\.names` and \`\.scriptfilespecs`. The scripts - are passed as a comma separated array. Ignoring initial spaces is done using - undelimited-delimited argument pair trick. - \_cod - -\_def\.DDDscripts#1{\.DDDscriptsA#1,,,\.end} -\_def\.DDDscriptsA#1#2,{\_ifx,#1\_ea\.untilend\_else - \.isfiledefined{#1#2}\_iftrue% - \_addto\.scriptfilespecs{\.cs{filespec:#1#2} }% - \_addto\.names{\.cs{filename:#1#2} \.cs{filespec:#1#2} }% - \_fi - \_ea\.DDDscriptsA\_fi -} - - \_doc - For 3D views we need to process yet another comma separated list, this time - with \`\.DDDviews`. The result has to be separated by spaces and we also - don't want to emit something if the specified view was invalid. Unfortunately - this is expansion only context, so we can't issue a warning. - - As a user convenience, before `\.DDDview` is executed, view with the name of - `\.name` is tried instead of empty view array. This means that for simple 3D - art with one view, one can create view with the same name as the 3D object - and not have to specify anything. We also take the name only after it is - redefined from optional key-value parameters~-- this is so we can support - even the case of e.g. `screw` 3D model used twice, once with `name=screw1`, - another time with `name=screw2` (with the corresponding `screw1` and `screw2` - views). This is probably less useful, but\dots - \_cod - -\_def\.DDDviews#1{\.DDDviewsA#1,,,\.end} -\_def\.DDDviewsA#1#2,{\_ifx,#1\_ea\.untilend\_else - \.isdefined{3dview:#1#2}\_iftrue - \_lastnamedcs\_space \_fi - \_ea\.DDDviewsA\_fi -} - - \_doc - The activation/deactivation names are kind of cryptic, so we give them - descriptive names. Default is explicit (de)activation. Instead of `/PV` (page - visible) and `/PI` (page invisible) it would be possible to use \"page open" - and \"page close". These are slightly different in cases when more pages are - shown on screen at once, because only one page is \"open", while multiple are - \"visible". - \_cod - -\.sdef{activation:explicit}{/XA} -\.sdef{activation:auto}{/PV} -\.sdef{deactivation:explicit}{/XD} -\.sdef{deactivation:auto}{/PI} - - \_doc - Additional means of customization are here. \`\.RMdefaults` contains the - default key-value parameters. \`\.RMpresentationextra` can be used to set - more attributes in `/RichMediaPresentation` dictionary (although those are - more specific and not generally useful). - \_cod - -\_def\.RMdefaults{% - name=\.name, - activation=explicit, - deactivation=explicit, - toolbar=true, - views=, - scripts=, -} -\_def\.RMpresentationextra{} - - \_doc - For scripting using JavaScript actions one needs to access the 3D context of - the 3D / Rich Media annotation. This requires the page number. We can't use - `this.pageNum`, because the script strictly doesn't have to be on - the same page. We use `\.pageof` (`\.setpageof` was done in `\.boxedannot`) - to retrieve the page number in next run. Also PDF indexes page numbers from - 0. \`\DDDannot``{<name>}`. and \`\DDDcontext``{<name>}` allow this. - \_cod - -\_def\.DDDannot#1{% - this.getAnnotRichMedia(\_the\_numexpr\.pageof{rm:#1}-1\_relax, '#1')% -} -\_def\.DDDcontext#1{\.DDDannot{#1}.context3D} - -\_nspublic \DDDannot \DDDcontext ; - - \_doc - \label[mm-3dviews] - \secc 3D views - - This is the interesting part about 3D art. They can have a set of predefined - views~-- although a user may start from one, they can interactively change - all the aspects by dragging with mouse or messing with the settings shown by - right click menu. - - There are several transformations that have to be done before it is possible - to display 3D scene on a computer screen: - - \begitems \style n - * 3D transformation from the coordinate system of 3D artwork (\"model") to the \"world - coordinate system". - * 3D transformation from the world coordinate system to camera coordinate - system. - * projection to 2D (3D to 2D transformation). - \enditems - - When talking about PDF, positive $x$ goes to the right, positive $y$ up, and - positive $z$ \"away" from us (\"into the page"). This means we are working - with a left handed coordinate system. In camera space, the camera sits at - $(0, 0, 0)$ facing towards positive $z$ with positive $x$ and $y$ going right - and up respectively. Projection (one way or another) discards the $z$ - coordinate. - - Although the transformations are not strictly linear, they are essentially - done using multiplication by {\em transformation matrices}. The matrix for - \"model to world" (or \"model") transformation is part of the 3D art file and can't - be changed. However, we can make it up, because we can fully control the - second transformation (\"world to camera" or \"view" transformation)~-- - although we don't specify the \"world to camera" matrix but rather its - inverse, the \"camera to world" matrix (`/C2W`). This matrix has the $4 \times 4$ - form, which also allows {\em linear transformation} and {\em translation}: - - $$ - M_{c2w} = \pmatrix{a & d & g & t_x \cr b & e & h & t_y \cr c & f & i & t_z \cr 0 & 0 & 0 & 1} - $$ - - Here we use the column major convention, which is also the order how we would - write the matrix to PDF file, where it is an array of 12 elements: - - \begtt \catcode`\$=3 - /C2W [$a$ $b$ $c$ $d$ $e$ $f$ $g$ $h$ $i$ $t_x$ $t_y$ $t_z$] - \endtt - - In the rendering pipeline everything is transformed from world coordinates to - camera space coordinates. We can think about the process also in the other - way. Using $M_{c2w}$ we specify camera's position and orientation in the - world coordinate system. Due to how transformation using matrix - multiplication works, the first column in the $M_{c2w}$ matrix (vector $(a, - b, c)^T$) specifies how \"positive $x$ direcetion" (\"right") from camera - space ends up in world coordinate system. Similiarly for $(d, e, f)^T$ being - the image of positive $y$ (\"up") and $(g, h, i)^T$ being the image of - positive $z$ (\"forward"). The last column, $(t_x, t_y, t_z)^T$ represents - translation from camera space to world. Translation of origin (camera - position) will leave it in the point with coordinates $(t_x, t_y, t_z)$. - Because of these associations with the intuitive meanings of $x$, - $y$, $z$ in camera space we also sometimes call the vectors in the first - three columns of $M_{c2w}$ \"right", \"up" and \"forward" and the last one - \"eye": - - $$ - \vec{R} = \pmatrix{a \cr b \cr c}\!, \quad - \vec{U} = \pmatrix{d \cr e \cr f}\!, \quad - \vec{F} = \pmatrix{g \cr h \cr i}\!, \quad - \vec{E} = \pmatrix{t_x \cr t_y \cr t_z}\!. - $$ - - We usually want $\vec{R}$, $\vec{U}$ and $\vec{F}$ to form an orthonormal set - of vectors, i.e. all of unit length and each pair is orthogonal. The - orthoganility will come from the way we calculate them, but the normality has - to be ensured by normalizing the vectors after computing them, which will not - be explicitly written out in the following text. $\vec{E}$ is a positional, - not directional, vector and it's length will be preserved. - - Now we only need a convenient way to calculate all four vectors. A wide spread - method is sometimes called \"look at". It essentially involves having two - points: \"eye" ($E$, position of the camera) and \"target" ($T$, the point - where the camera is pointing at). The camera position is already provided: - $$ - \vec{E} = E - $$ - From these two points alone we can easily calculate the forward vector, which - corresponds to the direction of the camera: - $$ - \vec{F} = \overrightarrow{ET} = T - E - $$ - When we now imagine the point $E$ and vector $\vec{F}$ looking towards $T$ we - can see that there is a degree of freedom~-- the camera can rotate about the - forward vector. There is no other way than to arbitrarily choose - either up or right vector. Usually we choose an arbitrary \"global up" vector - $U_G$, which will influence the general direction of the final up vector. - This is because we use it to calculate the right vector: - $$ - \vec{R} = \vec{U}_G \times \vec{F} - $$ - The cross product makes it so that: - \begitems \style n - * $\vec{R}$ is perpendicular to $\vec{F}$ - * it is also perpendicular to global up vector ($\vec{U}_G$) which we used to - get rid of remaining degree of freedom. - \enditems - Now that we have two orthonormal vectors (with the normalization not being - explicit) we can calculate the remaining up vector: - $$ - \vec{U} = \vec{F} \times \vec{R} - $$ - - The last mysterious part about the calculation are the cross products. They - are of course not commutitative, so why e.g. $\vec{U}_G \times \vec{F}$ and - not the other way around? This is because we have to preserve the relations - these vectors had as directions of positive axes of the original camera - space. There we had positive $x$ going right, positive $y$ up and positive - $z$ forward in a left handed coordinate system. This means that following - holds (according the left hand rule): - - $$\eqalign{ - \vec{R} &= \vec{U} \times \vec{F} \cr % j * k = i - \vec{U} &= \vec{F} \times \vec{R} \cr % k * i = j - \vec{F} &= \vec{R} \times \vec{U} \cr % i * j = k - }$$ - - The scheme has one flaw though. When the directions of global up vector - $\vec{U}_G$ and forward vector $\vec{F}$ are linearly dependent the computed - right vector will be $(0, 0, 0)$. Hence some handling of this special case is - needed. - - The \"look at" method is essentially what is used in Alexander Grahn's - package movie15\fnote{\url{https://www.ctan.org/pkg/movie15}}. Although the - input aren't two points, but rather a \"center of orbit" point ($COO$, our - \"target"), \"center of orbit to camera vector" ($\overrightarrow{C2C}$, - default is $(0, -1, 0)$) and distance of camera from the center of orbit - ($ROO$). The value used for the arbitrary \"global up" vector is $(0, 0, 1)$. - When forward vector is $(0, 0, z)$, then global up is chosen to be $(0, -1, - 0)$ or $(0, 1, 0)$ to handle the \"0 right vector" issue. - - Because the movie15 method of providing the parameters is used in essentially - all packages that handle PDF 3D art (movie15, media9, rmannot, Con\TeX/t) we - also follow the suite. - - \`\DDDview`[<view name>][<key-value parameters>] is the command for defining - 3D views. These have to be saved into separate PDF objects anyways, using - this interface we allow their reuse. If <view name> is same as <name> of - `\RM` argument and no other views are specified <view name> view is - automatically used (see `\RM` for details). - - Key-value parameters are not optional this time, because rarely one suffices - with default values~-- different 3D views are about customization. Handling - of them is not as straightforward as before. We initially read the key-value - parameters only to determine the `method` used for calculating the `/C2W` - matrix. Then we reread key-value parameters again, this time with also with - the default values for this particular method. Not that the general 3d views - details are changed, but the methods themselves have key-value parameters of - their own, and we support specifying them in this \"flat" way. - - Additionally we allow the different methods used to compute `/C2W` to not be - expandable. Hence they are executed outside of expansion only context and are - fully processed~-- the text they add to 3D view PDF object is temporarily - stored in \`\.viewparams`. - - The rest is simply setting sensible defaults (or user overrides) for - internal/external name of the view (`/IN` and `/XN`, one is used for - scripting, one is shown by the PDF viewer), background color, rendering mode, - and lighting. Cross sections and nodes are currently not supported, although - users can hook in their own code using `\.DDDviewextra`, - `\.DDDrendermodeextra` or `\.DDDprojectionextra`. - - We have to be careful about setting rendering mode and lighting scheme, - because they normally fall back to the values specified in 3D art file, which - we can't access, so better not set them to anything if they are empty. - \_cod - -\_def\.DDDview[#1][#2]{\_bgroup - \_readkv{\.DDDviewdefaults,#2}% - \_edef\.tmp{\.DDDviewdefaults,\.cs{3dview:\_kv{method}:defaults}}% - \_readkv{\.tmp,#2}% - \.colortorgbdef\.bgcolor{\_kv{background}}% - \.cs{3dview:\_kv{method}}% sets \.viewparams (/MS, /C2W, /CO) - \_pdfobj {<</Type /3DView - /XN (#1) - /IN (#1) - \.viewparams % /MS, /C2W, /CO - /P << - \.cs{3dprojection:\_kv{projection}} - \.DDDprojectionextra - >> - /BG <<%/Type /3DBG - /Subtype /SC - /C [\.bgcolor] - >> - \.emptyor{\_kv{rendermode}}{% - /RM <<%/Type /3DRenderMode - /Subtype /\.nonempty - \.DDDrendermodeextra >> }% - \.emptyor{\_kv{lighting}}{% - /LS <<%/Type /3DLightingScheme - /Subtype /\.nonempty >> }% - >>}% - \_pdfrefobj\_pdflastobj - \.sxdef{3dview:#1}{\_the\_pdflastobj \_space 0 R}% - \_egroup -} - -\_nspublic \DDDview ; - - - \_doc - \`\.DDDviewdefaults` stores default key-value parameters for 3D views. They - are mostly the PDF standard defaults or what movie15/media9 uses (for - compatibility).\`\.DDDviewextra`, \`\.DDDrendermodeextra` or - \`\.DDDprojectionextra` can be used by the users to hook themself into 3D - view object creation. - \_cod - -\_def\.DDDviewdefaults{ - projection=perspective, - scale=1, - ps=Min, - FOV=30, - background=1 1 1, - rendermode=, - lighting=, - method=media9, -} -\_def\.DDDprojectionextra{} -\_def\.DDDrendermodeextra{} -\_def\.DDDviewextra{} - - \_doc - There are two different projection methods: - \begitems - * Orthographic: $z$ coordinate is simply thrown away, `scale` is used for scaling - the result. For technical parts where we want lines that are parallel stay - parallel in the view. - * Perspective: is the way human eye sees. `FOV` can be used to set field of - view. (`ps` parameter for additonal scaling to fit width/height is also - available, but the default is fine for casual users). - \enditems - \_cod - -\.sdef{3dprojection:ortho}{/Subtype /O /OS \_kv{scale}} -\.sdef{3dprojection:perspective}{/Subtype /P /FOV \_kv{FOV} /PS /\_kv{ps}} - - - \_doc - We offer the possibility of setting`/C2W` matrix and `/CO` (distance from - camera to center of orbit) directly using `method=manual`. - \_cod - -\.sdef{3dview:manual:defaults}{ - matrix=1 0 0 0 1 0 0 0 1 0 0 0 , - centeroforbit=0, -} -\.sdef{3dview:manual}{\_edef\.viewparams{ - /MS /M - /C2W [\_kv{matrix}] - /CO \_kv{centeroforbit} -}} - - \_doc - Another simple way of specifying camera position/orientation is to use a - named setting of U3D file using a U3D path with `method=u3d`. - \_cod - -\.sdef{3dview:u3d:defaults}{ - u3dpath=, -} -\.sdef{3dview:u3d}{% - \_pdfunidef\.tmp{\_kv{u3dpath}}% - \_edef\.viewparams{ - /MS /U3D - /U3DPath \.tmp \_space -}} - - \_doc - The most advanced method of setting `/C2W` matrix and `/CO` is - `method=media9`. It is thoroughly explained above, the few differences are - because the input values are not two points. Also for conciseness \"x", \"y" - and \"z" are used instead of right, up and forward. The calculations are - done in Lua, for simplicity. - - We expect the user to supply the numbers in the form \"`1 2 3`", but in Lua we - need them comma separated (\"1, 2, 3"). \`\.luatriplet` does this. Just in - case the code is somehow adapted without ensuring that $x$ and $z$ are - orthonormal, we normalize also $y$ after the second cross product. - \_cod - -\_def\.luatriplet#1 #2 #3 {#1, #2, #3} - -\.sdef{3dview:media9:defaults}{% - roo=0, - coo=0 0 0, - c2c=0 -1 0, -} -\.sdef{3dview:media9}{\_edef\.viewparams{ - /MS /M - /C2W [\_directlua{ - local function normalize(x, y, z) - local len = math.sqrt(x*x + y*y + z*z) - if len \csstring\~= 0 then return x/len, y/len, z/len else return 0, 0, 0 end - end - local function cross(ux, uy, uz, vx, vy, vz) - return uy*vz - uz*vy, uz*vx - ux*vz, ux*vy - uy*vx - end - local function printmat(...) - local arr = table.pack(...) - for k, v in ipairs(arr) do - arr[k] = string.format("\_pcent.6f", v) - end - tex.print(table.concat(arr, " ")) - end - - local roo = \_kv{roo} - local coo_x, coo_y, coo_z = \_ea\.luatriplet\_expanded{\_kv{coo}} - local c2c_x, c2c_y, c2c_z = normalize(\_ea\.luatriplet\_expanded{\_kv{c2c}}) - - local eye_x, eye_y, eye_z = coo_x + c2c_x*roo, coo_y + c2c_y*roo, coo_z + c2c_z*roo - - local z_x, z_y, z_z = -c2c_x, -c2c_y, -c2c_z - - local up_x, up_y, up_z = 0, 0, 1 - if math.abs(z_x) + math.abs(z_y) < 0.0000001 then % z_x == 0 and z_y == 0 - if z_z < 0.0000001 then % z_z <= 0 - up_x, up_y, up_z = 0, 1, 0 - else - up_x, up_y, up_z = 0, -1, 0 - end - end - - local x_x, x_y, x_z = normalize(cross(up_x, up_y, up_z, z_x, z_y, z_z)) - local y_x, y_y, y_z = normalize(cross(z_x, z_y, z_z, x_x, x_y, x_z)) - - local eye_x, eye_y, eye_z = coo_x - z_x*roo, coo_y - z_y*roo, coo_z - z_z*roo - - printmat(x_x, x_y, x_z, y_x, y_y, y_z, z_x, z_y, z_z, eye_x, eye_y, eye_z) - }] - /CO \_kv{roo} -}} - - \_doc - Last, but not least, is an action for setting the 3D view of a 3D/RM annotation - using an action. \`\.goto3dviewaction``[goto3dview:<name>:<view>]`. <name> is - name of the annotation which will be influenced. <view> is passed directly to - PDF. Therefore it can be either an index to the view array (starting at 0) or name - of view in parentheses~-- \"`(<view name>)`". - \_cod - -\.sdef{goto3dviewaction}[#1:#2:#3]{/S /GoTo3DView - /TA \.cs{rm:#2} 0 R - /V #3 -} - - \_doc - \label[mime] - \sec MIME type database - - This is the uninteresting MIME type database teased in section~\ref[files]. - Ideally this would only be a subset of what IANA defines at - \url{https://www.iana.org/assignments/media-types/media-types.xhtml}. - But there are additions like `model/u3d` and `model/prc`, which don't - seem to be official, yet. Other \"unofficial" MIME types are taken from - Mozilla's \"common" lists: - \begitems - * \url{https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types}. - * \url{https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types} - \enditems - - - \`\.mimetype``{<extension>}{<MIME type>}` is a shortcut of mapping - <extension> to <MIME type>. - \_cod - -\_def\.mimetype#1#2{\_sdef{_pdfextra_mimetype:#1}{#2}} - -\.mimetype{js}{application/javascript} -\.mimetype{pdf}{application/pdf} - -\.mimetype{prc}{model/prc} -\.mimetype{u3d}{model/u3d} - -\.mimetype{wav}{audio/x-wav} -\.mimetype{mp3}{audio/mpeg} -\.mimetype{opus}{audio/opus} - -\.mimetype{avi}{video/x-msvideo} -\.mimetype{mp4}{video/mp4} -\.mimetype{webm}{video/webm} - -\_endnamespace -\_endcode |