summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/xsavebox
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/xsavebox
Initial commit
Diffstat (limited to 'macros/latex/contrib/xsavebox')
-rw-r--r--macros/latex/contrib/xsavebox/ChangeLog56
-rw-r--r--macros/latex/contrib/xsavebox/README.md50
-rw-r--r--macros/latex/contrib/xsavebox/xsavebox.pdfbin0 -> 372998 bytes
-rw-r--r--macros/latex/contrib/xsavebox/xsavebox.sty392
-rw-r--r--macros/latex/contrib/xsavebox/xsavebox.tex199
5 files changed, 697 insertions, 0 deletions
diff --git a/macros/latex/contrib/xsavebox/ChangeLog b/macros/latex/contrib/xsavebox/ChangeLog
new file mode 100644
index 0000000000..b5470c78a2
--- /dev/null
+++ b/macros/latex/contrib/xsavebox/ChangeLog
@@ -0,0 +1,56 @@
+2019-08-13
+ * v0.14
+ * fix: existing \theXXX macros, such as LaTeX counters, could accidentally
+ be overwritten
+
+2019-06-24
+ * v0.13
+ * fix: dvipdfmx driver option fixed to work with current expl3
+
+2018-07-09
+ * v0.12
+ * fix: non-vanishing Re-run message
+
+2018-06-07
+ * v0.11
+ * fix: some cs now def'ed as `protected'; documentation fixed
+
+2018-03-29
+ * v0.10
+ * fix: preventing creation of unused XObjects; doc updated
+
+2018-01-16
+ * v0.9
+ * fix: README.md did not render correctly on CTAN
+
+2018-01-04
+ * v0.8
+ * documentation updated
+
+2017-06-11
+ * v0.7
+ * fix: failure due to change of \hbox:n in expl3 [2017/05/13]
+
+2017-03-23
+ * v0.6
+ * fix: pdfbase.sty related adjustments
+
+2017-03-16
+ * v0.5
+ * fix: compatibility issues with ocgx2 package; error when using \long
+ arguments for \xsavebox
+
+2017-02-03
+ * v0.4
+ * fix: RTL related (luaTeX)
+
+2017-01-30
+ * v0.3
+ * fix: usability in RTL typesetting context
+
+2016-03-07
+ * v0.2
+ * new: package option `margin=<dimension>' added
+
+2016-02-25
+ * initial release (v0.1) to CTAN
diff --git a/macros/latex/contrib/xsavebox/README.md b/macros/latex/contrib/xsavebox/README.md
new file mode 100644
index 0000000000..61d91bfa99
--- /dev/null
+++ b/macros/latex/contrib/xsavebox/README.md
@@ -0,0 +1,50 @@
+# The `xsavebox` LaTeX Package
+
+© 2016--`\today` Alexander Grahn
+
+https://gitlab.com/agrahn/xsavebox
+
+## Description
+
+This package defines commands for saving content that can be repeatedly placed into the document without replicating DVI/PDF code in the output file, allowing for smaller file size of the final PDF and improved content caching for faster display in certain PDF viewers. The method makes use of "Form XObjects" defined in the PDF specification.
+
+The user commands are modelled after the standard LaTeX commands `\savebox`, `\sbox`, `\usebox` and the `lrbox` environment.
+
+All common TeX engines and back-ends are supported:
+* pdfLaTeX, LuaLaTeX
+* LaTeX &rArr; dvips &rArr; ps2pdf/Distiller
+* (Xe)LaTeX &rArr; (x)dvipdfmx
+
+## User commands
+
+* content saving:
+
+````latex
+\xsavebox{<name>}[<width>][<position>]{...}
+\xsavebox*{<name>}[<width>][<position>]{...}
+
+\xsbox{<name>}{...}
+
+\begin{xlrbox}{<name>}...\end{xlrbox}
+\begin{xlrbox*}{<name>}...\end{xlrbox*}
+````
+
+`<name>` is an identifier (not a command!) composed of arbitrary non-active characters, including spaces and numbers. A command for the declaration of `<name>` does not exist.
+
+Starred (`*`) variants allow for colour injection (pdfLaTeX/LuaLaTeX only).
+
+* content insertion (referencing):
+
+````latex
+\xusebox{<name>}
+\the<name>
+````
+
+The second, short form is useable if `<name>` is composed of `[a-zA-Z]`.
+
+
+The package was written in the LaTeX3 syntax.
+
+## 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/xsavebox/xsavebox.pdf b/macros/latex/contrib/xsavebox/xsavebox.pdf
new file mode 100644
index 0000000000..15801a37c0
--- /dev/null
+++ b/macros/latex/contrib/xsavebox/xsavebox.pdf
Binary files differ
diff --git a/macros/latex/contrib/xsavebox/xsavebox.sty b/macros/latex/contrib/xsavebox/xsavebox.sty
new file mode 100644
index 0000000000..164dfab829
--- /dev/null
+++ b/macros/latex/contrib/xsavebox/xsavebox.sty
@@ -0,0 +1,392 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% xsavebox.sty
+%
+% Copyright 2016--\today, Alexander Grahn
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Provides commands for saving and repeating any content (typeset text, external
+% and inline [TikZ/PGF, PSTricks] graphics) similar to standard LaTeX's
+% \savebox, \sbox, \usebox commands and the `lrbox' environment, but without
+% code replication for smaller PDF output size
+%
+% content saving:
+%
+% (starred `*' variants allow for colour injection [pdflatex/lualatex only])
+%
+% \xsavebox{<name>}[<width>][<position>]{...}
+% \xsavebox*{<name>}[<width>][<position>]{...}
+%
+% \xsbox{<name>}{...}
+%
+% \begin{xlrbox}{<name>}...\end{xlrbox}
+% \begin{xlrbox*}{<name>}...\end{xlrbox*}
+%
+% content insertion:
+%
+% \xusebox{<name>}
+% \the<name> %short form of \xusebox{<name>} for <name> composed of [a-zA-Z]
+%
+% Supports all known engines and backends including
+% pdflatex, latex+dvips+ps2pdf, xelatex, latex+dvipdfmx, lualatex.
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://mirrors.ctan.org/macros/latex/base/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% 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{driver=dvipdfmx}{expl3}
+}
+\DeclareOptionX*{}\ProcessOptionsX*\relax %allow anything as an option
+
+\RequirePackage{xparse}
+\RequirePackage{l3keys2e}
+
+\def\g@xsb@version@tl{0.14}
+\def\g@xsb@date@tl{2019/08/13}
+\ProvidesExplPackage{xsavebox}{\g@xsb@date@tl}{\g@xsb@version@tl}
+{saveboxes for repeating content without code replication}
+
+\msg_set:nnnn{xsavebox}{support~outdated}{
+ Support~package~`#1'~too~old.
+}{
+ Please~install~an~up~to~date~version~of~`#1'.\\
+ Loading~xsavebox~will~abort!
+}
+
+%re-run message
+\msg_set:nnn{xsavebox}{rerun}{Rerun~to~get~internal~references~right!}
+\cs_new_protected:Nn\xsb_rerun_msg:{
+ \cs_if_exist:NF\g_xsb_rerunwarned_tl{
+ \tl_new:N\g_xsb_rerunwarned_tl
+ \AtEndDocument{\msg_warning:nn{xsavebox}{rerun}}
+ }
+}
+
+%unknown package option error message
+\msg_set:nnnn{xsavebox}{unknown~package~option}{Unknown~package~option~`#1'.}{
+ Package option~'#1'~is~unknown;\\
+ perhaps~it~is~spelled~incorrectly.
+}
+
+%package options
+\bool_new:N\g_xsb_dvisvgm_bool %`dvisvgm' driver option
+\tl_gset:Nn\g_xsb_margin_tl{3pt}
+\keys_define:nn{xsavebox}{
+ margin .code:n = {
+ \setlength\l_tmpa_dim{#1}
+ \tl_gset:Nx\g_xsb_margin_tl{\dim_use:N\l_tmpa_dim}
+ },
+
+ xetex .code:n = {}, %dummy, we use engine test instead
+
+ dvipdfmx .choice:,
+ dvipdfmx / true .code:n = {
+ \bool_gset_false:N\g_xsb_dvisvgm_bool
+ \PassOptionsToPackage{dvipdfmx}{pdfbase}
+ },
+ dvipdfmx / false .code:n = {},
+ dvipdfmx .default:n = {true},
+
+ dvisvgm .choice:,
+ dvisvgm / true .code:n = {
+ \bool_gset_true:N\g_xsb_dvisvgm_bool
+ \PassOptionsToPackage{dvisvgm}{pdfbase}
+ },
+ dvisvgm / false .code:n = {\bool_gset_false:N\g_xsb_dvisvgm_bool},
+ dvisvgm .default:n = {true},
+
+ unknown .code:n = {
+ \msg_error:nnx{xsavebox}{unknown~package~option}{\l_keys_key_tl}
+ }
+}
+\ProcessKeysOptions{xsavebox}
+
+\sys_if_output_pdf:T{\bool_gset_false:N\g_xsb_dvisvgm_bool}
+
+\RequirePackage{pdfbase}
+\@ifpackagelater{pdfbase}{2017/03/23}{}{
+ \msg_error:nnn{xsavebox}{support~outdated}{pdfbase.sty}
+ \tex_endinput:D
+}
+\cs_gset_eq:NN\xsb@newkey\pbs@newkey
+\cs_gset_eq:NN\xsb_pdfxform:nnnnn\pbs_pdfxform:nnnnn
+\cs_gset_eq:NN\xsb_pdflastxform:\pbs_pdflastxform:
+\cs_gset_eq:NN\xsb_pdfrefxform:n\pbs_pdfrefxform:n
+
+\bool_if:NTF\g_xsb_dvisvgm_bool{
+ \tl_gset:Nn\g_xsb_margin_tl{0pt}
+ \cs_new_protected_nopar:Nn\xsb_updatebbox:nnn{\special{dvisvgm:bbox~#1~#2~#3}}
+}{
+ \cs_new_protected_nopar:Nn\xsb_updatebbox:nnn{}
+}
+
+\int_new:N\g_xsb_id_int
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% content insertion (referencing, actually)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\DeclareDocumentCommand\xusebox{m}{
+ \tl_if_exist:cF{xsb_name_#1}{\msg_error:nnn{xsavebox}{save-box~undefined}{#1}}
+ \tl_use:c{the#1}
+}
+\msg_set:nnnn{xsavebox}{save-box~undefined}{
+ Save-box~`#1'~undefined~\msg_line_context:.
+}{
+ Save-box~`#1'~must~be~defined~/before/~use.
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% saving content
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\DeclareDocumentCommand\xsavebox{smO{\width}O{c}+m}{
+ \xsb_check_box_name:n{#2}
+ \group_begin:
+ \tl_set:Nn\l_xsb_content_grouped_tl{
+ \group_begin:
+ \cs_set:Nn\ocgxii_beginocg:nnnn{}\cs_set:Nn\ocgxii_endocg:{}
+ #5
+ \group_end:
+ }
+ \xsb_push_props: %new, empty properties dict
+ \xsb_beginLTR:
+ \IfBooleanTF{#1}{
+ %later colour injection possible (pdfTeX, LuaTeX)
+ \hbox_set:Nn\l_xsb_raw_box{\l_xsb_content_grouped_tl} %raw content
+ \hbox_set:Nn\l_xsb_box{ %content re-aligned
+ \makebox[#3][#4]{\l_xsb_content_grouped_tl}
+ }
+ }{
+ %the LaTeX way (no colour injection)
+ \sbox\l_xsb_raw_box{\l_xsb_content_grouped_tl} %raw content
+ \savebox\l_xsb_box[#3][#4]{\l_xsb_content_grouped_tl} %re-aligned
+ }
+ %process one of \l_xsb_raw_box or \l_xsb_box
+ \dim_compare:nTF{\box_wd:N\l_xsb_raw_box>\box_wd:N\l_xsb_box}{
+ \str_if_eq:eeTF{#4}{s}{
+ %sqeezing content correctly
+ \IfBooleanTF{#1}{
+ %for colour injection
+ \hbox_set:Nn\l_xsb_box{
+ \makebox[\box_wd:N\l_xsb_raw_box][l]{
+ \makebox[#3][s]{#5}
+ }
+ }
+ }{
+ %no colour injection
+ \savebox\l_xsb_box[\box_wd:N\l_xsb_raw_box][l]{
+ \makebox[#3][s]{#5}
+ }
+ }
+ \xsb_process_box:nnnN{#2}{#3}{#4}\l_xsb_box
+ }{ % raw content
+ \IfBooleanTF{#1}{
+ \hbox_set:Nn\l_xsb_raw_box{#5}
+ }{
+ \sbox\l_xsb_raw_box{#5}
+ }
+ \xsb_process_box:nnnN{#2}{#3}{#4}\l_xsb_raw_box
+ }
+ }{
+ \IfBooleanTF{#1}{
+ \hbox_set:Nn\l_xsb_box{ %content re-aligned
+ \makebox[#3][#4]{#5}
+ }
+ }{
+ \savebox\l_xsb_box[#3][#4]{#5} %re-aligned
+ }
+ \xsb_process_box:nnnN{#2}{\width}{c}\l_xsb_box
+ }
+ \xsb_endLTR:
+ \group_end:
+}
+
+\DeclareDocumentCommand\xsbox{m+m}{\xsavebox{#1}{#2}}
+
+\DeclareDocumentEnvironment{xlrbox}{m}{
+ \xsb_check_box_name:n{#1}
+ \xsb_xlrbox:
+}{
+ \xsb_endxlrbox:n{#1}
+}
+
+\DeclareDocumentEnvironment{xlrbox*}{m}{
+ \xsb_check_box_name:n{#1}
+ \xsb_xlrbox_star:
+}{
+ \xsb_endxlrbox_star:n{#1}
+}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\msg_set:nnn{xsavebox}{name~in~use}{
+ The~name~`#1'~is~already~in~use~\msg_line_context:.\\
+ Select~a~different~box~name.
+}
+\cs_new_protected_nopar:Nn\xsb_check_box_name:n{
+ \bool_if:nTF{
+ \cs_if_exist_p:c{the#1} && !\tl_if_exist_p:c{xsb_name_#1}
+ }{
+ \msg_error:nnx{xsavebox}{name~in~use}{#1}
+ }{
+ \tl_clear_new:c{xsb_name_#1}
+ }
+}
+
+\cs_new_protected:Nn\xsb_xlrbox:{
+ \group_begin:
+ \xsb_push_props: %new, empty properties dict
+ \xsb_beginLTR:
+ \hbox_set:Nw\l_xsb_box\group_begin:
+ \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn\xsb_endxlrbox:n{
+ \unskip
+ \group_end:\hbox_set_end:
+ \sbox\l_xsb_box{\hbox_unpack_drop:N\l_xsb_box}
+ \xsb_process_box:nnnN{#1}{\width}{c}\l_xsb_box
+ \xsb_endLTR:
+ \group_end:
+}
+
+\cs_new_protected:Nn\xsb_xlrbox_star:{
+ \group_begin:
+ \xsb_push_props: %new, empty properties dict
+ \xsb_beginLTR:
+ \hbox_set:Nw\l_xsb_box
+ \ignorespaces
+}
+
+\cs_new_protected_nopar:Nn\xsb_endxlrbox_star:n{
+ \unskip
+ \hbox_set_end:
+ \xsb_process_box:nnnN{#1}{\width}{c}\l_xsb_box
+ \xsb_endLTR:
+ \group_end:
+}
+
+\cs_new_protected_nopar:Nn\xsb_process_box:nnnN{
+ %measure natural dimensions
+ \cs_set_nopar:Npn\width {\box_wd:N#4}
+ \cs_set_nopar:Npn\height{\box_ht:N#4}
+ \cs_set_nopar:Npn\depth {\box_dp:N#4}
+ \cs_set_nopar:Npn\totalheight{\dimexpr\height+\depth\relax}
+ \tl_set:Nx\l_xsb_wd_tl{\dim_use:N\width}
+ \tl_set:Nx\l_xsb_ht_tl{\dim_use:N\height}
+ \tl_set:Nx\l_xsb_dp_tl{\dim_use:N\depth}
+ %evaluate width argument (allowing for calc-type expressions)
+ \setlength\l_tmpa_dim{#2}
+ \tl_set:Nx\l_xsb_new_wd_tl{\dim_use:N\l_tmpa_dim}
+ %temporarily (for distilling) push the box bounds somewhat; glyphs tend to
+ %be bigger than their bounding boxes
+ \hbox_set_to_wd:Nnn#4{\width+\g_xsb_margin_tl+\g_xsb_margin_tl}{
+ \hss\hbox_unpack_drop:N#4\hss
+ }
+ \box_set_ht:Nn#4{\height+\g_xsb_margin_tl}
+ \box_set_dp:Nn#4{\depth+\g_xsb_margin_tl}
+ \sys_if_output_dvi:TF{
+ \tl_if_exist:cF{xsb@\int_use:N\g_xsb_id_int}{
+ \tl_gset:cn{xsb@\int_use:N\g_xsb_id_int}{true}
+ \xsb_rerun_msg:
+ }
+ }{
+ \tl_gset:cn{xsb@\int_use:N\g_xsb_id_int}{true}
+ }
+ \xsb_pop_props_to:N\l_tmpa_tl
+ %distill box to Form XObject, if used (ref'ed)
+ \bool_if:cT{c_\tl_use:c{xsb@\int_use:N\g_xsb_id_int}_bool}{
+ \xsb_pdfxform:nnnnn{1}{0}{\l_tmpa_tl}{
+ \cs_if_exist_use:N\ocgbase_insert_oc:}{#4}
+ }
+ %for tracking box usage
+ \iow_now:Nx\@mainaux{
+ \token_to_str:N\xsb@newkey{xsb@\int_use:N\g_xsb_id_int}{false}
+ }
+ %define command for inserting the m-boxed XObject reference
+ \cs_gset_protected:cpx{the#1}{
+ \exp_not:N\tl_if_exist:cF{xsb_\int_use:N\g_xsb_id_int}{
+ %mark box as `used'
+ \exp_not:N\iow_now:Nx\@mainaux{
+ \token_to_str:N\xsb@newkey{xsb@\int_use:N\g_xsb_id_int}{true}
+ }
+ \exp_not:N\tl_new:c{xsb_\int_use:N\g_xsb_id_int}
+ }
+ \exp_not:N\bool_if:cF{c_\tl_use:c{xsb@\int_use:N\g_xsb_id_int}_bool}{
+ \exp_not:N\xsb_rerun_msg:
+ }
+ \exp_not:N\xsb_beginLTR:
+ \xsb_updatebbox:nnn{\l_xsb_new_wd_tl}{\l_xsb_ht_tl}{\l_xsb_dp_tl}
+ \exp_not:N\makebox[\l_xsb_new_wd_tl][#3]{
+ \exp_not:N\hbox_to_wd:nn{\l_xsb_wd_tl}{
+ \exp_not:N\vrule~width~\c_zero_dim~height~\l_xsb_ht_tl~
+ depth~\l_xsb_dp_tl
+ \exp_not:N\skip_horizontal:n{-\g_xsb_margin_tl}
+ \bool_if:cT{c_\tl_use:c{xsb@\int_use:N\g_xsb_id_int}_bool}{
+ \exp_not:N\xsb_pdfrefxform:n{\xsb_pdflastxform:}
+ }
+ \hss
+ }
+ }
+ \exp_not:N\xsb_endLTR:
+ }
+ \int_gincr:N\g_xsb_id_int
+}
+
+\box_new:N\l_xsb_box %for saving the re-aligned content
+\box_new:N\l_xsb_raw_box %for saving the raw content
+
+%environment for setting LTR typesetting direction with e-TeX based engines
+\cs_new_protected:Nn\xsb_beginLTR:{
+ \cs_if_exist:NT\TeXXeTstate{
+ \int_compare:nT{\TeXXeTstate>\c_zero_int}{\beginL}
+ }
+}
+\cs_new_protected:Nn\xsb_endLTR:{
+ \cs_if_exist:NT\TeXXeTstate{
+ \int_compare:nT{\TeXXeTstate>\c_zero_int}{\endL}
+ }
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%for nested xsavebox/xlrbox cmds/envs, maintain a stack of /Properties
+%dictionaries, mostly used for marked-content property lists, e. g. OCGs;
+%(see PDF Reference)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\seq_new:N\g_xsb_props_seq
+
+%push new, empty properties dictionary on the stack (to be used by THIS pkg)
+\cs_new_protected:Nn\xsb_push_props:{\seq_gpush:Nn\g_xsb_props_seq{}}
+
+%pop current properties dict from stack and place its contents as a
+%/Properties << <prop dict> >> entry into the macro argument
+\cs_new_protected:Nn\xsb_pop_props_to:N{
+ \seq_gpop:NNT\g_xsb_props_seq\l_tmpa_tl{
+ \tl_trim_spaces:N\l_tmpa_tl
+ \str_if_eq:eeF{\l_tmpa_tl}{}{\tl_set:Nx#1{/Properties<<\l_tmpa_tl>>}}
+ }
+}
+
+%add property (usually /key/val entry) to current properties dict;
+%this command is meant to be used by OTHER packages
+\cs_new_protected:Nn\xsb_addto_props:n{
+ \seq_gpop:NNT\g_xsb_props_seq\l_tmpa_tl{
+ \tl_put_right:Nn\l_tmpa_tl{#1~}
+ \seq_gpush:Nx\g_xsb_props_seq{\l_tmpa_tl}
+ }
+}
+
+%get stack size; meant to be used by OTHER packages
+\cs_new:Nn\xsb_count_props:{\seq_count:N\g_xsb_props_seq}
diff --git a/macros/latex/contrib/xsavebox/xsavebox.tex b/macros/latex/contrib/xsavebox/xsavebox.tex
new file mode 100644
index 0000000000..1739f1f072
--- /dev/null
+++ b/macros/latex/contrib/xsavebox/xsavebox.tex
@@ -0,0 +1,199 @@
+% Copyright 2007--\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.
+%
+\documentclass[a4paper]{article}
+\frenchspacing
+\usepackage[UKenglish]{babel}
+\usepackage{xsavebox}
+\usepackage{csquotes}
+\usepackage{graphicx}
+\graphicspath{{files/}}
+\usepackage{color}
+%\usepackage{lmodern}
+ \usepackage[tt=false]{libertine} %override beramono (doesn't look like tt font)
+ \usepackage[libertine]{newtxmath}
+\usepackage[T1]{fontenc}
+\usepackage[protrusion,expansion]{microtype}
+\usepackage{parskip}
+\usepackage[bookmarksnumbered,linktocpage]{hyperref}
+\usepackage[ocgcolorlinks]{ocgx2}
+\usepackage{fancyvrb}
+\clubpenalty=10000
+\widowpenalty=10000
+\displaywidowpenalty=10000
+\renewcommand{\textfraction}{0.0}
+\renewcommand{\topfraction}{1.0}
+\renewcommand{\bottomfraction}{1.0}
+
+\hypersetup{
+ pdftitle={The xsavebox Package},
+ pdfsubject={Documentation},
+ pdfauthor={Alexander Grahn},
+ pdfkeywords={PDF features, XObject, savebox, LaTeX, dvips, repeated content, repeated graphics, eps, PostScript, ps2pdf, XeLaTeX, pdfLaTeX, LuaLaTeX, dvipdfmx, dvisvgmx},
+ citebordercolor={0 0 1},
+ filebordercolor={0 0 1},
+ linkbordercolor={0 0 1},
+ menubordercolor={0 0 1},
+ pagebordercolor={0 0 1},
+ urlbordercolor={0 0 1},
+% pdfpagelayout=SinglePage
+}
+
+%\usepackage[scaled=0.85]{luximono}
+
+\def\XeLaTeX{X\kern-.1667em\lower.5ex\hbox{\reflectbox{E}}\kern-.125em\LaTeX}
+\def\XeTeX{X\kern-.1667em\lower.5ex\hbox{\reflectbox{E}}\kern-.125em\TeX}
+\def\pXepLaTeX{(X\kern-.1667em\lower.5ex\hbox{\reflectbox{E}})\kern-.125em\LaTeX}
+
+\def\parsedate#1/#2/#3\relax{
+ \def\year{#1}
+ \def\month{#2}
+ \def\day{#3}
+}
+
+\begin{document}
+\makeatletter
+\title{The {\sffamily xsavebox} Package, v\g@xsb@version@tl}
+\expandafter\parsedate\g@xsb@date@tl\relax %set current date to package date
+\makeatother
+\author{Alexander Grahn\\[1ex]\url{https://gitlab.com/agrahn/xsavebox}}
+\maketitle
+\begin{abstract}
+ \noindent This package defines commands for saving content that can be repeatedly placed into the document without replicating DVI/PDF code in the output file, allowing for smaller size of the final PDF file and improved content caching for faster display in certain PDF viewers. The user commands are modelled after the standard \LaTeX{} commands \verb+\savebox+, \verb+\sbox+, \verb+\usebox+ and the `\verb+lrbox+' environment. The package supports all common \TeX{} engines and back-ends, including `\verb+dvips+', `\verb+(x)dvipdfmx+' and `\verb+dvisvgm+'.
+\end{abstract}
+
+\section{Introduction}
+Whenever the standard \LaTeX{} command \verb+\usebox{+\emph{save-box}\verb+}+ is issued to insert a previously defined \emph{save-box} more than once, the typeset content stored therein is written as DVI or PDF code into the output file again. The redundant code adds to the overall file size and may impair the page caching facilities built into some PDF viewers.
+
+The PDF file format defines a powerful mechanism for packing readily typeset content once into self-contained entities, so-called \enquote{Form XObjects}, that can be referenced at other places within the PDF document.
+
+The `\verb+xsavebox+' package makes this PDF feature accessible on the \LaTeX{} level as a set of user commands which look similar to and are used in a similar way as the well-known \emph{save-box} related \LaTeX{} commands.
+
+All common \TeX{} engines and back-ends are supported, which are:
+\begin{itemize}
+%\item $\varepsilon$-\TeX
+\item pdf\LaTeX, Lua\LaTeX,
+\item \LaTeX{} $\rightarrow$ \verb+dvips+ $\rightarrow$ \verb+ps2pdf+/Distiller
+\item \pXepLaTeX{} $\rightarrow$ \verb+(x)dvipdfmx+
+\item \pXepLaTeX{} $\rightarrow$ \verb+dvisvgm+
+\end{itemize}
+
+To enable `\verb+dvipdfmx+' or `\verb+dvisvgm+', pass them as document class option.
+
+It should be emphasized that \enquote{Form XObjects} is a PDF feature. Content saved and referenced using \enquote{Form XObjects} is only visible in the final PDF output, but not in intermediate formats of the work-flow if those are involved, namely DVI and PostScript. Of course, PostScript converted back from PDF displays the content correctly.
+
+\section{Package Options}
+\begin{Verbatim}
+margin=<dimension>
+\end{Verbatim}
+When content is converted into a Form XObject, it is clipped to its bounding box. However, the font glyphs used for typesetting tend to be slightly bigger than their boxes. In order to avoid clipping, the `\verb+xsavebox+' package \emph{temporarily} adds an additional margin of \verb+3pt+ around the content. In rare cases, \verb+3pt+ may turn out to be insufficient, e.\,g. if text is up-scaled before being put into a save box. This option allows the setting of a larger margin to ensure that the content gets entirely into the save box. Note that whichever value is chosen for `\verb+margin+', the spacing of the boxes, when inserted into the document, remains unchanged. This is what every user definitely wants.
+
+\section{User commands}
+\subsection*{Content saving}
+\vspace{-1\baselineskip}
+\hrulefill\\[-1.6\baselineskip]
+\begin{Verbatim}
+\xsbox{<xsbox name>}{<content>}
+\xsavebox{<xsbox name>}[<width>][<position>]{<content>}
+\xsavebox*{<xsbox name>}[<width>][<position>]{<content>}
+
+\begin{xlrbox}{<xsbox name>}
+ <content>
+\end{xlrbox}
+
+\begin{xlrbox*}{<xsbox name>}
+ <content>
+\end{xlrbox*}
+\end{Verbatim}
+\vspace{-1\baselineskip}
+\hrulefill\\[-1\baselineskip]
+
+As for usage, the main difference of these commands as compared to their standard \LaTeX{} counterparts without the leading `\verb+x+' is the way of naming boxes. The label \verb+<xsbox name>+ is an identifier that may be composed of arbitrary non-active characters, such as letters, numbers, spaces, punctuation marks. A command for declaring a box register does not exist; \verb+<xsbox name>+ is created upon using above-listed commands and environments. Also, an existing \verb+<xsbox name>+ can be re-used, which simply overwrites its old with new content. The package keeps track of box usage; the content of a box is only written to the output file if it is referenced at least once later in the document.
+
+The \verb+[<width>]+ and \verb+[<position>]+ options have the same meaning as with \verb+\savebox+ and \verb+\makebox+. As usual, the additional length commands
+\begin{Verbatim}
+ \width
+ \height
+ \depth
+ \totalheight
+\end{Verbatim}
+are defined for use in the \verb+[<width>]+ option and refer to the original dimensions of \verb+<content>+. The value of \verb+<position>+ may assume one of `\verb+l+', `\verb+r+', `\verb+c+' or `\verb+s+'. The default is `\verb+c+' for text centred in the box.
+
+\verb+<content>+ is typeset in LR-mode. Longer text to be typeset in paragraph mode must be put into a \verb+\parbox+ or `\verb+minipage+'.
+
+The starred (`\verb+*+') versions of the commands allow for later colour injection into the boxes at the place of their referencing. The colour which is active at the time of building the box is not saved with the content. This feature only works with pdf\LaTeX{} and Lua\LaTeX.
+
+Note that
+\begin{Verbatim}
+ \xsbox{image for frequent use}{\includegraphics{example}}
+\end{Verbatim}
+is particularly useful in the \LaTeX{} $\rightarrow$ \verb+dvips+ $\rightarrow$ \verb+ps2pdf+ work-flow. Although the other engines and back-ends already take care of preventing multiple graphics file inclusion, a noticeable reduction of compilation time will be achieved in general.
+
+Verbatim content can only be saved using the `\verb+xlrbox[*]+' environment.
+
+With \LaTeX{} in DVI mode and \XeLaTeX, box \emph{saving} commands should not be placed on a line of their own with empty lines above and below. For technical reasons this will produce an empty paragraph. Always place them at the beginning or at the end of a paragraph in the input file. Also, box saving commands cannot be placed in the document preamble with \LaTeX{} (DVI) and \XeLaTeX{}.
+
+In a right-to-left typesetting context (RTL) using the (pdf)\LaTeX{} or \XeLaTeX{} engines, the \verb+<content>+ argument should be enclosed in a pair of \verb+\beginR+ and \verb+\endR+ commands for correct typesetting results.
+
+\subsection*{Referencing saved content}
+Previously saved content can be inserted with\\[-1.6\baselineskip]
+
+\hrulefill\\[-1.6\baselineskip]
+\begin{Verbatim}
+\xusebox{<xsbox name>}
+\end{Verbatim}
+\vspace{-1ex}
+or
+\vspace{-1ex}
+\begin{Verbatim}
+\the<xsbox name>
+\end{Verbatim}
+\vspace{-1\baselineskip}
+\hrulefill\\[-1\baselineskip]
+
+The second, shorthand form can be used if \verb+<xsbox name>+ is composed exclusively of letters (`\verb+a+`--`\verb+z+', `\verb+A+`--`\verb+Z+'). For example, a box named `\verb+MyFirstExample+' could be referenced as
+\begin{Verbatim}
+ \theMyFirstExample
+\end{Verbatim}
+but a box named `\verb+My 1st Example ;-)+' would require
+\begin{Verbatim}
+ \xusebox{My 1st Example ;-)}
+\end{Verbatim}
+
+The referencing commands \verb+\xusebox{<xsbox name>}+ and \verb+\the<xsbox name>+ can again be placed inside the \verb+<content>+ body of box saving commands. There is no upper limit of nesting levels.
+
+\verb+\xusebox{<xsbox name>}+ and \verb+\the<xsbox name>+ behave exacly like common \TeX{} boxes. Therefore, they can be scaled, rotated and resized using the corresponding commands from the `\verb+graphicx+' package.
+
+\section{Example}
+An example with colour injection (pdf\LaTeX/Lua\LaTeX-only) follows:
+
+\begin{xlrbox*}{SavedPar}
+ \begin{minipage}[b]{1in}
+ silly boxed paragraph that no one will ever use
+ \end{minipage}
+\end{xlrbox*}%
+Here is a \fbox{\color{blue}\theSavedPar} for anything.
+
+The same \fbox{\color{green}\rotatebox{90}{\theSavedPar}} was inserted again but with a different colour and rotated by 90 degree.
+
+\begin{Verbatim}
+\usepackage{xsavebox}
+\usepackage{color}
+\usepackage{graphicx}
+...
+\begin{xlrbox*}{SavedPar}% `*' --> no colour at the time of saving
+ \begin{minipage}[b]{1in}
+ silly boxed paragraph that no one will ever use
+ \end{minipage}
+\end{xlrbox*}
+%colours injected into \theSavedPar
+Here is a \fbox{\color{blue}\theSavedPar} for anything.
+
+The same \fbox{\color{green}\rotatebox{90}{\theSavedPar}} was
+inserted again but with a different colour and rotated by 90 degree.
+\end{Verbatim}
+
+\end{document}