%%% A set of macros for various transformations of TeX boxes. %%% (bases on plain and pdf(e)TeX primitives) % % Version: 2.4 % Author: Pawe{\l} Jackowski (P.Jackowski@gust.org.pl) % Public Domain % % The macro provides a bunch of TeX box transformations. It was initially % inspired by trans.tex (BOP, bop@bop.com.pl), remade to work with pdf(e)TeX. % % Files: % pdf-trans.tex % example.tex % % History: % 05.2004, v1.0b % * first embryo % 11.2004, v1.0 % * modified, released % 08.2005, v2.0 % * general rework; % - fraction-based trigonometry % - pdfliteral dimensions rounding % - some other small changes % 09.2005, v2.1 % * some adds; % - \setbpround <0..4> sets pdf dimen rounding precision % - \boxgobble macro swallows and returns \hbox{} % 10.2005, v2.2 % * rework; % - \bboxtrans vs \cboxtrans switches % - \boxext* stuff; yet another scaling approach % % 10.2013, v2.3 % * nasty bug in \roundbponce; a sign was dropped when the number % had no integer part (-0.99234 was rounded to 0.99). In current % implementation small negative numbers might be rounded to -0.0 % (minus sign remains) % * \roundbponce renamed to \round:bp:once % % 01.2014, v2.4 % * some rework on pdf unit related macros % - \t@bp renamed to \asbp as pretty useful converter to big points (with no rounding) % - new macro \roundbpto{0..4}{} rounds #2 to #1 digits % - \roundbp now always rounds to \pdfdecimaldigits % - \enablebpround, \disablebpround and \setbpround now redefine \tobp: % - \enablebpround makes \tobp equivalent to \roundbp % - \disablebpround makes \tobp equivalent to \asbp % - \setbpround{0..4} makes \tobp equivalent to \roundbpto{#1} % - removed \romannumeral from csnames, decimal digits used instead % - removed \big:p@ints \def\starttrans{% \xdef\endtrans{% \catcode`\noexpand\@=\the\catcode`\@ \catcode`\noexpand\:=\the\catcode`\: }\catcode`\@=11 \catcode`\:=11 } \starttrans % Each transformation macro defined below expands to (h)box. Thus, can be used % whenever (h)box can. Each must be followed by the box that is to be % transformed. This scheme allows to cumulate transformations, that are applied % from right to left. For instance, % \boxrotatebb{30}\boxscale{300}\hbox{Aqq} % first enlarges boxed `Aqq' three times, then rotates it 30 degrees. Thanks % to \transboxcheck trick (see below), \copy and \box can be used as well as % \hbox, \vbox, \vtop. However, the consequence is that the transformed box % can't be void (\boxinfo definition is an exception here). \transbox register % contains the box being transformed. \newbox\transbox % Whenever we perform relative dimension scaling, a number provided as a macro % parameter is divided by \transfactor. Default is percentage (ie. 75 -> 0.75). % One may notice, that we often perform the same operation (ie. division) % several times instead of defining \transfactor as a float-like string. In % example, if there are two dimens, \X and \Y, and both are to be scaled by % 0.23, we always say `\numexpr\X*23/\transfactor' and % `\numexpr\Y*23/\transfactor' instead of 0.23\X 0.23\Y. In some cases it gives % significant preciseness improvement. \newcount\transfactor \transfactor=100 % plus some internal variables \newdimen\trans:dim \newdimen\trans:dim:a \newdimen\trans:dim:b \newdimen\trans:dim:c \newdimen\trans:dim:d \newcount\trans:count \def\trans:def{} \def\trans:def:a{} \def\trans:def:b{} \def\trans:def:c{} \def\trans:def:d{} % Here the main trick starts. Each macro usually expands to \hbox\bgroup..., % defines \transboxtodo procedure, assigns the following box, then performs % \transboxtodo and ends with \egroup. If the box being transformed is generic % `\hbox{', TeX finishes the assignment just after opening `{', while the box being % assigned (\transbox) is still void. Thus, we check \ifvoid\transbox, and if % so, we put \transboxtodo on \aftergroup stack to let TeX finish box % assignment. Obviously it crashes, if one tries to transform a void box. % So, if you see 'Missing } inserted' error, you are probably trying to transform % a void box. You can use \boxinfo to check the situation. \def\transboxini{% \afterassignment\transboxcheck \setbox\transbox} \def\transboxcheck{% \ifvoid\transbox \expandafter\aftergroup \fi\transboxtodo} % When some macro changes box dimensions directly (i.e. \ht\transbox=0pt), % we may need to be sure that transformed box behaves like \hbox. \def\transhboxini{% \afterassignment\transhboxcheck \setbox\transbox} \def\transhboxcheck{% \ifvoid\transbox \expandafter\aftergroup\expandafter\transhboxwrap \else \expandafter\transhboxwrap \fi} \def\transhboxwrap{% \ifvbox\transbox \setbox\transbox\hbox{\box\transbox}% \fi \transboxtodo} \long\def\transboxdef#1\transboxend{% \bgroup\def\transboxtodo{#1\egroup}\transboxini} \long\def\transhboxdef#1\transboxend{% \bgroup\def\transboxtodo{#1\egroup}\transhboxini} % Sometimes we need to keep bounding box untouched and transform box content % only (or vice versa). Each transformation that affects both the bounding box % (via resizing \ht|\wd|\dp) and the box content itself (via \pdfliteral) % checks \iftransbbox and \iftranscbox flags. No practical reasons to affect % \boxrotatebb by this feature, however. \newif\iftransbbox \newif\iftranscbox \transbboxtrue \transcboxtrue \long\def\transbcboxdef#1\transbboxdef#2\transcboxdef#3\transboxend{% \bgroup\edef\transboxtodo{% \unexpanded{#1}% \iftransbbox\unexpanded{#2}\fi \iftranscbox\unexpanded{#3}\else\box\transbox\fi \egroup}\transhboxini} \def\bboxtranson{% \hbox\bgroup\transbboxtrue \def\transboxtodo{\box\transbox\egroup}\transboxini} \def\bboxtransoff{% \hbox\bgroup\transbboxfalse \def\transboxtodo{\box\transbox\egroup}\transboxini} \def\cboxtranson{% \hbox\bgroup\transcboxtrue \def\transboxtodo{\box\transbox\egroup}\transboxini} \def\cboxtransoff{% \hbox\bgroup\transcboxfalse \def\transboxtodo{\box\transbox\egroup}\transboxini} \long\def\bboxtrans#1{\iftranscbox\cboxtransoff#1\cboxtranson\else#1\fi} \long\def\cboxtrans#1{\iftransbbox\bboxtransoff#1\bboxtranson\else#1\fi} % User-defined operation on \transbox. Remember, that \transboxcheck assumes % initially void \transbox, so #1 should contain something like \box\tranbox, % \unvbox\tranbox, \setbox\transbox\voidb@x etc. \def\hboxtrans{\hbox\transboxdef} \def\vboxtrans{\vbox\transboxdef} \def\vtoptrans{\vtop\transboxdef} \let\boxtrans\hboxtrans % flip \def\boxflipx{% \hbox\transboxdef \savebp\trans:def\wd\transbox \pdfliteral{q -1 0 0 1 \trans:def\space 0 cm}% \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxflipy{% \hbox\transboxdef \savebp\trans:def\wd\transbox \pdfliteral{q 1 0 0 -1 0 \tobp{\ht\transbox-\dp\transbox} cm}% \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxflipxy{% \hbox\transboxdef \savebp\trans:def\wd\transbox \pdfliteral{q -1 0 0 -1 \trans:def\space \tobp{\ht\transbox-\dp\transbox} cm}% \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \let\boxflip\boxflipxy % \boxflipy plus \ht\transbox <-> \dp\transbox exchange \def\boxflipbase{% \hbox\transbcboxdef \transbboxdef \trans:dim=\ht\transbox \ht\transbox=\dp\transbox \dp\transbox=\trans:dim \transcboxdef \pdfliteral{q 1 0 0 -1 0 0 cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} % scale by factor \def\boxscalexy#1#2{% \hbox\transbcboxdef \transbboxdef \wd\transbox=\dimexpr\wd\transbox*(#1)/\transfactor\relax \ht\transbox=\dimexpr\ht\transbox*(#2)/\transfactor\relax \dp\transbox=\dimexpr\dp\transbox*(#2)/\transfactor\relax \transcboxdef \edef\trans:def:a{\fdivide{#1}\transfactor}% \edef\trans:def:b{\fdivide{#2}\transfactor}% \pdfliteral{q \trans:def:a\space 0 0 \trans:def:b\space 0 0 cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxscale#1{\boxscalexy{#1}{#1}} \def\boxscalex#1{\boxscalexy{#1}\transfactor} \def\boxscaley#1{\boxscalexy\transfactor{#1}} \let\boxscalez\boxscale % scale to dimen \def\boxscaleto#1#2#3#4{% \hbox\transbcboxdef \trans:dim:a=\dimexpr#1\relax \trans:dim:b=\dimexpr#2\relax \trans:dim:c=\dimexpr#3\relax \trans:dim:d=\dimexpr#4\relax \transbboxdef \wd\transbox=\dimexpr\wd\transbox*\trans:dim:a/\trans:dim:b\relax \ht\transbox=\dimexpr\ht\transbox*\trans:dim:c/\trans:dim:d\relax \dp\transbox=\dimexpr\dp\transbox*\trans:dim:c/\trans:dim:d\relax \transcboxdef \edef\trans:def:a{\fdivide\trans:dim:a\trans:dim:b}% \edef\trans:def:b{\fdivide\trans:dim:c\trans:dim:d}% \pdfliteral{q \trans:def:a\space 0 0 \trans:def:b\space 0 0 cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxscalexyto#1#2{\boxscaleto{#1}{\wd\transbox}{#2}{\ht\transbox+\dp\transbox}} \def\boxscalexto#1{\boxscaleto{#1}{\wd\transbox}{1sp}{1sp}} \def\boxscaleyto#1{\boxscaleto{1sp}{1sp}{#1}{\ht\transbox+\dp\transbox}} \def\boxscalehtto#1{\boxscaleto{1sp}{1sp}{#1}{\ht\transbox}} \def\boxscaledpto#1{\boxscaleto{1sp}{1sp}{#1}{\dp\transbox}} \let\boxscalewdto\boxscalexto % scale \wd|\ht|\dp to dimen, others uniformly \def\boxuniscaleto#1#2{% \hbox\transbcboxdef \trans:dim:a=\dimexpr#1\relax \trans:dim:b=\dimexpr#2\relax \transbboxdef \wd\transbox=\dimexpr\wd\transbox*\trans:dim:a/\trans:dim:b\relax \ht\transbox=\dimexpr\ht\transbox*\trans:dim:a/\trans:dim:b\relax \dp\transbox=\dimexpr\dp\transbox*\trans:dim:a/\trans:dim:b\relax \transcboxdef \edef\trans:def:a{\fdivide\trans:dim:a\trans:dim:b}% \pdfliteral{q \trans:def:a\space 0 0 \trans:def:a\space 0 0 cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxuniscalexto#1{\boxuniscaleto{#1}{\wd\transbox}} \def\boxuniscaleyto#1{\boxuniscaleto{#1}{\ht\transbox+\dp\transbox}} \def\boxuniscalehtto#1{\boxuniscaleto{#1}{\ht\transbox}} \def\boxuniscaledpto#1{\boxuniscaleto{#1}{\dp\transbox}} \let\boxuniscalewdto\boxuniscalexto % yet another scaling approach; extend \wd|\ht|\dp do dimen, scale accordingly \def\boxextscaleto#1#2#3{% \hbox\transbcboxdef \trans:dim:a=\wd\transbox \trans:dim:b=\dimexpr#1\relax \trans:dim:c=\dimexpr\ht\transbox+\dp\transbox\relax \trans:dim:d=\dimexpr#2+#3\relax \trans:dim=\dp\transbox \transbboxdef \wd\transbox=\trans:dim:b \ht\transbox=\dimexpr#2\relax \dp\transbox=\dimexpr#3\relax \transcboxdef \edef\trans:def:a{\fdivide\trans:dim:b\trans:dim:a}% \edef\trans:def:b{\fdivide\trans:dim:d\trans:dim:c}% \savebp\trans:def:c\dimexpr-\dp\transbox+(\dp\transbox+\ht\transbox)*\trans:dim/\trans:dim:c\relax \pdfliteral{q \trans:def:a\space 0 0 \trans:def:b\space 0 \trans:def:c\space cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxextscale#1#2#3{\boxextscaleto{\wd\transbox+#1}{\ht\transbox+#2}{\dp\transbox+#3}} % resizing; the result may be different for hboxes and vboxes; % since we have \boxextscaleto plus \iftransbbox and \iftranscbox flags, % the following two macros are obsolete by now \def\boxresizeto#1#2#3{% \hbox\transboxdef \ifx\relax#1\relax\else \wd\transbox=\dimexpr#1\relax \fi \ifx\relax#2\relax\else \ht\transbox=\dimexpr#2\relax \fi \ifx\relax#3\relax\else \dp\transbox=\dimexpr#3\relax \fi \box\transbox \transboxend} \def\boxresize#1#2#3{% \hbox\transboxdef \ifx\relax#1\relax\else \wd\transbox=\dimexpr\wd\transbox+#1\relax \fi \ifx\relax#2\relax\else \ht\transbox=\dimexpr\ht\transbox+#2\relax \fi \ifx\relax#3\relax\else \dp\transbox=\dimexpr\dp\transbox+#3\relax \fi \box\transbox \transboxend} % extents; to keep things consistent for negative extents, \dp\transbox becomes 0pt \def\boxextents#1#2#3#4{% \hbox\transboxdef \kern\dimexpr#1\relax \vbox{% \kern\dimexpr#3\relax \box\transbox \kern\dimexpr#4\relax }% \kern\dimexpr#2\relax \transboxend} \def\boxhextent#1#2{\boxextents{#1}{#2}\z@\z@} \def\boxvextent#1#2{\boxextents\z@\z@{#1}{#2}} \def\boxextent#1{\boxextents{#1}{#1}{#1}{#1}} % and yet another approach; append extents to box content \def\boxexts#1#2#3#4{% \hbox\transboxdef \trans:dim:a=\wd\transbox \trans:dim:b=\dimexpr\trans:dim:a+#1+#2\relax \trans:dim:c=\dimexpr\ht\transbox+\dp\transbox\relax \trans:dim:d=\dimexpr\trans:dim:c+#3+#4\relax \edef\trans:def:a{\fdivide\trans:dim:b\trans:dim:a}% \edef\trans:def:b{\fdivide\trans:dim:d\trans:dim:c}% \savebp\trans:def:c-\dimexpr#1\relax \savebp\trans:def:d\dimexpr-#4+(#3+#4)*\dp\transbox/\trans:dim:c\relax \pdfliteral{q \trans:def:a\space 0 0 \trans:def:b\space \trans:def:c\space \trans:def:d\space cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxhext#1#2{\boxexts{#1}{#2}\z@\z@} \def\boxvext#1#2{\boxexts\z@\z@{#1}{#2}} \def\boxext#1{\boxexts{#1}{#1}{#1}{#1}} % raw translation \def\boxtranslate#1#2{% \hbox\transboxdef \pdfliteral{q 1 0 0 1 \tobp{#1} \tobp{#2} cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} % heuristic rotation by 90 degrees (left and right variants) \def\boxrevolveleft{% \hbox\transbcboxdef \trans:dim:a=\wd\transbox \trans:dim:b=\ht\transbox \transbboxdef \wd\transbox=\dimexpr\ht\transbox+\dp\transbox\relax \ht\transbox=\trans:dim:a \dp\transbox=\z@ \transcboxdef \pdfliteral{q 0 1 -1 0 \tobp{\trans:dim:b} 0 cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxrevolveright{% \hbox\transbcboxdef \trans:dim:a=\wd\transbox \trans:dim:b=\dp\transbox \transbboxdef \wd\transbox=\dimexpr\ht\transbox+\dp\transbox\relax \ht\transbox=\trans:dim:a \dp\transbox=\z@ \transcboxdef \pdfliteral{q 0 -1 1 0 \tobp{\trans:dim:b} \tobp{\trans:dim:a} cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \let\boxrevolvepi\boxflipxy % clockwise rotation relative to base point \def\boxrotate#1{% \hbox\transboxdef \floatsincos\trans:def:a\trans:def:b{#1}% \pdfliteral{q \trans:def:b\space \negbp\trans:def:a\space % clockwise \trans:def:a\space % vs anti clockwise \trans:def:b\space 0 0 cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} % clockwise rotation relative to base point translated (x,y) \def\boxrotatexy#1#2#3{% \hbox\transboxdef \floatsincos\trans:def:a\trans:def:b{#1}% \savebp\trans:def:c\dimexpr#2\relax \savebp\trans:def:d\dimexpr#3\relax \pdfliteral{q \trans:def:b\space \negbp\trans:def:a\space \trans:def:a\space \trans:def:b\space \trans:def:c\space \trans:def:d\space cm 1 0 0 1 \negbp\trans:def:c\space \negbp\trans:def:d\space cm}% ? \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \def\boxrotatec#1{\boxrotatexy{#1}{\wd\transbox/2}{(\ht\transbox-\dp\transbox)/2}} \def\boxrotatell#1{\boxrotatexy{#1}\z@{-\dp\transbox}} \def\boxrotatelr#1{\boxrotatexy{#1}{\wd\transbox}{-\dp\transbox}} \def\boxrotateul#1{\boxrotatexy{#1}\z@{\ht\transbox}} \def\boxrotateur#1{\boxrotatexy{#1}{\wd\transbox}{\ht\transbox}} % bbox-wise rotation (left and right variants) \newif\ifbboxright \def\box:rotate:bb#1{% \trans:dim:a=\wd\transbox \trans:dim:b=\ht\transbox \trans:dim:c=\dp\transbox \trans:dim:d=\dimexpr\ht\transbox+\dp\transbox\relax \trans:count=\reducetrigangle{#1}\fractperiod\relax \ifcase\fracttrigfourth\trans:count\relax \fr@ct:sin:cos:i\trans:def:a\trans:def:b\trans:count \wd\transbox=\dimexpr\fr@ct:mul\trans:dim:a\trans:def:b +\fr@ct:mul\trans:dim:d\trans:def:a\relax \ht\transbox=\dimexpr\fr@ct:mul\trans:dim:b\trans:def:b\relax \dp\transbox=\dimexpr\fr@ct:mul\trans:dim:a\trans:def:a +\fr@ct:mul\trans:dim:c\trans:def:b\relax \savebp\trans:def:c=\dimexpr\fr@ct:mul\trans:dim:c\trans:def:a\relax \or \fr@ct:sin:cos:ii\trans:def:a\trans:def:b\trans:count \wd\transbox=\dimexpr-\fr@ct:mul\trans:dim:a\trans:def:b +\fr@ct:mul\trans:dim:d\trans:def:a\relax \ht\transbox=\dimexpr-\fr@ct:mul\trans:dim:c\trans:def:b\relax \dp\transbox=\dimexpr-\fr@ct:mul\trans:dim:b\trans:def:b +\fr@ct:mul\trans:dim:a\trans:def:a\relax \savebp\trans:def:c=\dimexpr-\fr@ct:mul\trans:dim:a\trans:def:b +\fr@ct:mul\trans:dim:c\trans:def:a\relax \or \fr@ct:sin:cos:iii\trans:def:a\trans:def:b\trans:count \wd\transbox=\dimexpr-\fr@ct:mul\trans:dim:a\trans:def:b -\fr@ct:mul\trans:dim:d\trans:def:a\relax \ht\transbox=\dimexpr-\fr@ct:mul\trans:dim:a\trans:def:a -\fr@ct:mul\trans:dim:c\trans:def:b\relax \dp\transbox=\dimexpr-\fr@ct:mul\trans:dim:b\trans:def:b\relax \savebp\trans:def:c=\dimexpr-\fr@ct:mul\trans:dim:a\trans:def:b -\fr@ct:mul\trans:dim:b\trans:def:a\relax \or \fr@ct:sin:cos:iv\trans:def:a\trans:def:b\trans:count \wd\transbox=\dimexpr\fr@ct:mul\trans:dim:a\trans:def:b -\fr@ct:mul\trans:dim:d\trans:def:a\relax \ht\transbox=\dimexpr\fr@ct:mul\trans:dim:b\trans:def:b -\fr@ct:mul\trans:dim:a\trans:def:a\relax \dp\transbox=\dimexpr\fr@ct:mul\trans:dim:c\trans:def:b\relax \savebp\trans:def:c=\dimexpr-\fr@ct:mul\trans:dim:b\trans:def:a\relax \fi \ifbboxright \trans:dim:d=\dimexpr\fr@ct:mul\trans:dim:a\trans:def:a\relax \ht\transbox=\dimexpr\ht\transbox+\trans:dim:d\relax \dp\transbox=\dimexpr\dp\transbox-\trans:dim:d\relax \savebp\trans:def:d\trans:dim:d \else \def\trans:def:d{0}% \fi \edef\trans:def:a{\fr@ct:div\trans:def:a}% \edef\trans:def:b{\fr@ct:div\trans:def:b}% \pdfliteral{q \trans:def:b\space \negbp\trans:def:a\space \trans:def:a\space \trans:def:b\space \trans:def:c\space \trans:def:d\space cm}% \savebp\trans:def=\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}} \def\boxrotatebbl#1{% \hbox\transboxdef \bboxrightfalse \box:rotate:bb{#1}% \transboxend} \def\boxrotatebbr#1{% \hbox\transboxdef \bboxrighttrue \box:rotate:bb{#1}% \transboxend} % the default is left variant \bboxrightfalse \def\boxrotatebb#1{% \hbox\transboxdef \box:rotate:bb{#1}% \transboxend} % slanting; \boxslantx{}\boxslanty{} is NOT equivalent to \boxslantxy{}{} \def\boxslant#1#2{% \hbox\transboxdef \fractsincos\trans:def:a\trans:def:b{#1}% \fractsincos\trans:def:c\trans:def:d{#2}% \edef\trans:def:a{\fdivide\trans:def:a\trans:def:b}% \edef\trans:def:c{\fdivide\trans:def:c\trans:def:d}% \pdfliteral{q 1 \trans:def:c\space \trans:def:a\space 1 0 0 cm}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} \let\boxslantxy\boxslant \def\boxslantx#1{\boxslant{#1}{0}} % not \z@!! \def\boxslanty#1{\boxslant{0}{#1}} % bounding box wise slanting (left and right variant) \def\box:slant:bb#1#2{% \trans:dim:a=\wd\transbox \trans:dim:b=\ht\transbox \trans:dim:c=\dp\transbox \trans:dim:d=\dimexpr\ht\transbox+\dp\transbox\relax \trans:count=\reducetrigangle{#1}{2*\fractfourth}\relax \ifcase\fracttrigfourth\trans:count\relax \fr@ct:sin:cos:i\trans:def:a\trans:def:b\trans:count \wd\transbox=\dimexpr\trans:dim:a+\trans:dim:d*\trans:def:a/\trans:def:b\relax \savebp\trans:def:c=\dimexpr\trans:dim:c*\trans:def:a/\trans:def:b\relax \or \fr@ct:sin:cos:ii\trans:def:a\trans:def:b\trans:count \wd\transbox=\dimexpr\trans:dim:a-\trans:dim:d*\trans:def:a/\trans:def:b\relax \savebp\trans:def:c=\dimexpr-\trans:dim:b*\trans:def:a/\trans:def:b\relax \fi \edef\trans:def{\fdivide\trans:def:a\trans:def:b}% \trans:count=\reducetrigangle{#2}{2*\fractfourth}\relax \ifcase\fracttrigfourth\trans:count\relax \fr@ct:sin:cos:i\trans:def:a\trans:def:b\trans:count \ht\transbox=\dimexpr\trans:dim:b+\trans:dim:a*\trans:def:a/\trans:def:b\relax \or \fr@ct:sin:cos:ii\trans:def:a\trans:def:b\trans:count \dp\transbox=\dimexpr\trans:dim:c-\trans:dim:a*\trans:def:a/\trans:def:b\relax \fi \ifbboxright \trans:dim:d=\dimexpr-\trans:dim:a*\trans:def:a/\trans:def:b\relax \ht\transbox=\dimexpr\ht\transbox+\trans:dim:d\relax \dp\transbox=\dimexpr\dp\transbox-\trans:dim:d\relax \savebp\trans:def:d\trans:dim:d \else \def\trans:def:d{0}% \fi \edef\trans:def:a{\fdivide\trans:def:a\trans:def:b}% \pdfliteral{q 1 \trans:def:a\space \trans:def\space 1 \trans:def:c\space \trans:def:d\space cm}% \savebp\trans:def=\wd\transbox \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}} \def\boxslantbbl#1#2{% \hbox\transboxdef \bboxrightfalse \box:slant:bb{#1}{#2}% \transboxend} \def\boxslantbbr#1#2{% \hbox\transboxdef \bboxrighttrue \box:slant:bb{#1}{#2}% \transboxend} \def\boxslantbb#1#2{% \hbox\transboxdef \box:slant:bb{#1}{#2}% \transboxend} \def\boxslantbbx#1{\boxslantbb{#1}{0}} % not \z@!! \def\boxslantbby#1{\boxslantbb{0}{#1}} \def\boxslantbbry#1{\boxslantbbr{0}{#1}} \def\boxslantbbly#1{\boxslantbbl{0}{#1}} % on-fly conversion to XObject (\pdfxform) \def\boxxform{% \hbox\transboxdef \immediate\pdfxform\transbox \pdfrefxform\pdflastxform \transboxend} \def\boxxformspec#1\boxxform{% \hbox\transboxdef \immediate\pdfxform#1\transbox \pdfrefxform\pdflastxform \transboxend} % Some previous version of pdftrans had \boxclip defined as \boxpath{}{W n}. This didn't % work properly since \boxpath restores the graphic state before placing the box itself % (see below). % clipping \def\boxclip{% \hbox\transboxdef \savebp\trans:def\wd\transbox \pdfliteral{q 0 \tobp{-\dp\transbox} \trans:def\space \tobp{\ht\transbox+\dp\transbox} re W n}% \box\transbox \pdfliteral{Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} % up and down \def\boxraise#1{% \hbox\transboxdef \raise\dimexpr#1\relax\box\transbox \transboxend} \def\boxlower#1{% \hbox\transboxdef \lower\dimexpr#1\relax\box\transbox \transboxend} \let\boxbaselineup\boxlower \let\boxbaselinedown\boxraise \def\boxbaselineat#1{% baseline at #1 of the total box vdim \hbox\transboxdef \lower\dimexpr(\ht\transbox+\dp\transbox)*(#1)/\transfactor-\dp\transbox\relax \box\transbox \transboxend} \def\boxmoveleft#1{% \vbox\transboxdef \moveleft\dimexpr#1\relax\box\transbox \transboxend} \def\boxmoveright#1{% \vbox\transboxdef \moveright\dimexpr#1\relax\box\transbox \transboxend} % rule-like spec of the box size (risky hack) \def\b@x:rule#1#2#{% #1\transboxdef \setbox0\hbox{\vrule width\wd\transbox height\ht\transbox depth\dp\transbox #2}% \wd\transbox=\wd0 \ht\transbox=\ht0 \dp\transbox=\dp0 \box\transbox \transboxend#1} \def\hboxr{\b@x:rule\hbox} \def\vboxr{\b@x:rule\vbox} \def\vtopr{\b@x:rule\vtop} \let\boxr\hboxr % reboxing; restore box natural dimensions (without shrink or stretch). \def\revbox{% \vbox\transboxdef \unvbox\transbox \transboxend} \def\revtop{% \vtop\transboxdef \unvbox\transbox \transboxend} \def\rehbox{% \hbox\transboxdef \unhbox\transbox \transboxend} % just wrap \def\hboxwrap{% \hbox\transboxdef \box\transbox \transboxend} \def\vboxwrap{% \vbox\transboxdef \box\transbox \transboxend} \def\vtopwrap{% \vtop\transboxdef \box\transbox \transboxend} % show boxes \def\boxshow#1#2#3{% \hbox\transboxdef \savebp\trans:def:a\wd\transbox \savebp\trans:def:b\dimexpr\ht\transbox+\dp\transbox\relax \savebp\trans:def:c\dp\transbox \box\transbox \pdfliteral{% q #1 1 0 0 1 \negbp\trans:def:a\space 0 cm 0 \negbp\trans:def:c\space \trans:def:a\space \trans:def:b\space re S #2 0 0 m \trans:def:a\space 0 l S #3 Q}% \transboxend} \def\boxsh{\boxshow{0 0 1 RG}{0 0 .8 RG [2 2]1 d}{}} % box painted according to graphic state parameters (ie. on layer). \def\boxgs#1#2{% \hbox\transboxdef \pdfliteral{q #1}% \savebp\trans:def\wd\transbox \box\transbox \pdfliteral{#2 Q 1 0 0 1 \trans:def\space 0 cm}% \transboxend} % markers around the box \def\boxmarkers#1#2#3{% \hbox\transboxdef \copy\transbox \trans:dim:a=\dimexpr#1\relax \trans:dim:b=\dimexpr#2\relax \pdfliteral{q #3}% \savebp\trans:def-\dp\transbox \box:markers:h \savebp\trans:def\ht\transbox \box:markers:h \savebp\trans:def-\wd\transbox \box:markers:v \savebp\trans:def\z@ \box:markers:v \pdfliteral{S Q}% \setbox\transbox\box\voidb@x \transboxend} \def\box:markers:h{% \savebp\trans:def:a\trans:dim:a \savebp\trans:def:b\trans:dim:b \pdfliteral{% \trans:def:a\space\trans:def\space m \trans:def:b\space\trans:def\space l}% \savebp\trans:def:a\dimexpr-\wd\transbox-\trans:dim:a\relax \savebp\trans:def:b\dimexpr-\wd\transbox-\trans:dim:b\relax \pdfliteral{% \trans:def:a\space\trans:def\space m \trans:def:b\space\trans:def\space l}} \def\box:markers:v{% \savebp\trans:def:a\dimexpr-\dp\transbox-\trans:dim:a\relax \savebp\trans:def:b\dimexpr-\dp\transbox-\trans:dim:b\relax \pdfliteral{% \trans:def\space \trans:def:a\space m \trans:def\space \trans:def:b\space l}% \savebp\trans:def:a\dimexpr\ht\transbox+\trans:dim:a\relax \savebp\trans:def:b\dimexpr\ht\transbox+\trans:dim:b\relax \pdfliteral{% \trans:def\space \trans:def:a\space m \trans:def\space \trans:def:b\space l}} % for sake of compatibility... \def\boxm#1#2{\boxmarkers{#1}{#2}{}} \let\boxmarks\boxmarkers % simple phantom \def\boxphantom{% \hbox\transboxdef \hbox to\wd\transbox {\vrule width\z@ height\ht\transbox depth\dp\transbox\hss}% \transboxend} % simple smash \def\boxsmash{% \hbox\transhboxdef \wd\transbox=\z@ \ht\transbox=\z@ \dp\transbox=\z@ \box\transbox \transboxend} \def\hboxsmash{% \hbox\transhboxdef \wd\transbox=\z@ \box\transbox \transboxend} \def\vboxsmash{% \vbox\transhboxdef \ht\transbox=\z@ \dp\transbox=\z@ \box\transbox \transboxend} \def\boxgobble{% \hbox\transboxdef % \global\setbox\transbox=\box\voidb@x \transboxend} % say something about the following box (this one can be followed void box) \def\box:about#1{% \hbox\bgroup \def\transboxtodo{% \trans:dim:a=\wd\transbox \trans:dim:b=\ht\transbox \trans:dim:c=\dp\transbox \box\transbox \hbox to\z@{\hss \hbox to\trans:dim:a{\hss \lower\trans:dim:c\vbox to\z@{\vss \vbox to\dimexpr\trans:dim:b+\trans:dim:c{\vss\tt #1\vbox{\vskip1ex \halign{\hskip1ex plus 1fil####&####\hskip1ex plus 1fil\cr \trans:def\span\cr wd & \the\trans:dim:a\cr ht & \the\trans:dim:b\cr dp & \the\trans:dim:c\cr}% \vskip1ex }% \vss}% }% \hss}% }% \egroup}\def\trans:def{}\def\trans:def:a{}\box:@bout} \def\box:@bout#1{% \ifcase \ifx#1\hbox 0 \else \ifx#1\vbox 1 \else \ifx#1\vtop 2 \else \ifx#1\box 3 \else \ifx#1\copy 4 \else 5 \fi\fi\fi\fi\fi \edef\trans:def{\trans:def\string\hbox}\expandafter\transboxini\or \edef\trans:def{\trans:def\string\vbox}\expandafter\transboxini\or \edef\trans:def{\trans:def\string\vtop}\expandafter\transboxini\or \edef\trans:def{\trans:def\string\box}\expandafter\boxabout:register\or \edef\trans:def{\trans:def\string\copy}\expandafter\boxabout:register\or \ifx#1\trans:def:a\errmessage{`#1' is not a box}\fi\let\trans:def:a#1% endless loop otherwise \edef\trans:def{\trans:def\string#1->}\expandafter\expandafter\expandafter\box:@bout\fi #1} \def\boxabout:register#1{% \let\trans:def:a#1% \afterassignment\boxabout:r@gister\trans:count} \def\boxabout:r@gister{% \edef\trans:def{\trans:def\the\trans:count\space (\ifvoid\trans:count void\else \ifhbox\trans:count hbox\else \ifvbox\trans:count vbox\fi\fi\fi)}% \afterassignment\transboxtodo \setbox\transbox\trans:def:a\trans:count} \def\boxinfo{\box:about{\boxpath{.3 w 0 G 0 0 .3 0 k}{B}}} \def\boxabout#1{\box:about{\boxgs{#1}{}}} % paint box path as a background of the box \def\boxpath#1#2{% \hbox\transboxdef \savebp\trans:def:a\wd\transbox \savebp\trans:def:b\dimexpr\ht\transbox+\dp\transbox\relax \savebp\trans:def:c\dp\transbox \pdfliteral{q #1 0 \negbp\trans:def:c\space \trans:def:a\space \trans:def:b\space re #2 Q}% \box\transbox \transboxend} \def\boxroundpath#1#2#3{% \hbox\transboxdef \savebp\trans:def:a\wd\transbox \savebp\trans:def:b\ht\transbox \savebp\trans:def:c\dp\transbox \trans:dim:d=\dimexpr#1\relax \savebp\trans:def:d\trans:dim:d \pdfliteral{q #2}% \pdfliteral{0 \trans:def:d\space m}% \savebp\trans:def\dimexpr\ht\transbox-\trans:dim:d \relax \pdfliteral{% 0 \trans:def\space l 0 \trans:def:b\space \trans:def:d\space \trans:def:b\space y}% \savebp\trans:def\dimexpr\wd\transbox-\trans:dim:d \relax \pdfliteral{\trans:def\space \trans:def:b\space l}% \savebp\trans:def\dimexpr\ht\transbox-\trans:dim:d \relax \pdfliteral{% \trans:def:a\space \trans:def:b\space \trans:def:a\space \trans:def\space y}% \savebp\trans:def\dimexpr\dp\transbox-\trans:dim:d \relax \pdfliteral{\trans:def:a\space \negbp\trans:def\space l}% \savebp\trans:def\dimexpr\wd\transbox-\trans:dim:d \relax \pdfliteral{% \trans:def:a\space \negbp\trans:def:c\space \trans:def\space \negbp\trans:def:c\space y \trans:def:d\space \negbp\trans:def:c\space l}% \savebp\trans:def\dimexpr\dp\transbox-\trans:dim:d \relax \pdfliteral{% 0 \negbp\trans:def:c\space 0 \negbp\trans:def\space y 0 \trans:def:d\space l}% \pdfliteral{h #3 Q}% \box\transbox \transboxend} \def\boxedgypath#1#2#3{% \hbox\transboxdef \savebp\trans:def:a\wd\transbox \savebp\trans:def:b\ht\transbox \savebp\trans:def:c\dp\transbox \trans:dim:d=\dimexpr#1\relax \savebp\trans:def:d\trans:dim:d \pdfliteral{q #2}% \pdfliteral{0 \trans:def:d\space m}% \savebp\trans:def\dimexpr\ht\transbox-\trans:dim:d \relax \pdfliteral{0 \trans:def\space l \trans:def:d\space \trans:def:b\space l}% \savebp\trans:def\dimexpr\wd\transbox-\trans:dim:d \relax \pdfliteral{\trans:def\space \trans:def:b\space l}% \savebp\trans:def\dimexpr\ht\transbox-\trans:dim:d \relax \pdfliteral{\trans:def:a\space \trans:def\space l}% \savebp\trans:def\dimexpr\dp\transbox-\trans:dim:d \relax \pdfliteral{\trans:def:a\space \negbp\trans:def\space l}% \savebp\trans:def\dimexpr\wd\transbox-\trans:dim:d \relax \pdfliteral{\trans:def\space \negbp\trans:def:c\space l \trans:def:d\space \negbp\trans:def:c\space l}% \savebp\trans:def\dimexpr\dp\transbox-\trans:dim:d \relax \pdfliteral{0 \negbp\trans:def\space l 0 \trans:def:d\space l}% \pdfliteral{h #3 Q}% \box\transbox \transboxend} % obsolete \let\boxsquarepath\boxedgypath %%% ARITHMETIC % some shortcuts \def\expandnumberafter#1#2{\expandafter#1\expandafter{\number#2}} \def\expandtwonumbersafter#1#2#3{% \expandafter#1\expandafter {\number#2\expandafter}\expandafter {\number#3}} \def\expandthreenumbersafter#1#2#3#4{% \expandafter#1\expandafter {\number#2\expandafter}\expandafter {\number#3\expandafter}\expandafter {\number#4}} \def\expandnumexprafter#1#2{\expandafter#1\expandafter{\number\numexpr#2}} \def\expandtwonumexprafter#1#2#3{% \expandafter#1\expandafter {\number\numexpr#2\expandafter}\expandafter {\number\numexpr#3}} \def\expandthreenumexprafter#1#2#3#4{% \expandafter#1\expandafter {\number\numexpr#2\expandafter}\expandafter {\number\numexpr#3\expandafter}\expandafter {\number\numexpr#4}} \def\expanddimexprafter#1#2{\expandafter#1\expandafter{\the\dimexpr#2}} % Whenever we write a dimen into PDF code, we need to convert it from TeX units % to Postscript big points. We handle that as precise as possible, using the % fact that eTeX handles 64bit numbers as temporary results of expressions such % as A*B/C. A is dimension in points, B is \pt:f@ctor and C is \bp:f@ctor. Note % that factor 100pt/100bp is more precise than 1pt/1bp or 10pt/10bp, but NOT % less precise than 1000pt/1000bp. Thus, 100 is the optimum. \edef\pt:f@ctor{\number\dimexpr100pt} % NOT \dimexpr100\p@! \edef\bp:f@ctor{\number\dimexpr100bp} % \begingroup \catcode`\P=12 \catcode`\T=12 \lccode`P=`p \lccode`T=`t \lowercase{\gdef\with@ut:pt#1PT{#1}} \endgroup \def\withoutpt{\expandafter\with@ut:pt} \def\negbp#1{\withoutpt\the\dimexpr-#1pt\relax} \def\asbp#1{\withoutpt\the\dimexpr#1*\pt:f@ctor/\bp:f@ctor\relax} % If we assume that \pdfdecimaldigits never exceeds possible range (0..4), % we can implement PDF dimens rounding in the following way: % \def\roundbp#1{% % \expandafter\expandafter % \csname r@und:bp:\the\pdfdecimaldigits\expandafter\endcsname % \expandafter\with@ut:pt\the\dimexpr(#1)*\pt:f@ctor/\bp:f@ctor\relax0000\relax} % or with less \expandafters \def\roundbp#1{% \expandafter\r@undbp\the\dimexpr(#1)*\pt:f@ctor/\bp:f@ctor\relax0000\relax} \def\r@undbp{% \csname r@und:bp:\the\pdfdecimaldigits\expandafter\endcsname \with@ut:pt} \expandafter\def\csname r@und:bp:0\endcsname #1.#2#3\relax{\number\numexpr#1#2/10\relax} \expandafter\def\csname r@und:bp:1\endcsname #1.#2#3#4\relax{\round:bp:once{#1}{#2#3}\relax} \expandafter\def\csname r@und:bp:2\endcsname #1.#2#3#4#5\relax{\round:bp:once{#1}{#2#3#4}\relax} \expandafter\def\csname r@und:bp:3\endcsname #1.#2#3#4#5#6\relax{\round:bp:once{#1}{#2#3#4#5}\relax} \expandafter\def\csname r@und:bp:4\endcsname #1.#2#3#4#5#6#7\relax{\round:bp:once{#1}{#2#3#4#5#6}\relax} \def\round:bp:once#1#2{% % 15.10.2013: that is wrong! that drops minus sign in case -0.xxxx %\number\numexpr#1\ifnum#1<0-\else+\fi %(\m@ne+\expandafter\r@und:bp:once\number\numexpr1#2/10\relax} % that works but may produce -0.0 \ifnum#11<0-\number\numexpr-\else\number\numexpr\fi #1+(\m@ne+\expandafter\r@und:bp:once\number\numexpr1#2/10\relax} % 21.01.2014: but both rounds 0.9995bp to 0.999, while they should to 1.0, % but this is a cost of bp<->pt conversion \def\r@und:bp:once#1#2\relax{#1)\relax\ifnum#2>0.#2\fi} % To change rounding digits and speed-up a little one may say \setbpround 0..4 \def\set:bp:rounder#1#2{% 0..4 \expandafter\edef\csname #1:\the\numexpr#2\relax\endcsname##1{% \unexpanded{\expandafter\expandafter\expandafter}\expandafter\noexpand \csname r@und:bp:\the\numexpr#2\relax\endcsname \unexpanded{\expandafter\with@ut:pt\the}% \dimexpr(##1)*\unexpanded{\pt:f@ctor/\bp:f@ctor}\relax0000\relax}} \set:bp:rounder{roundbpto}{0} \set:bp:rounder{roundbpto}{1} \set:bp:rounder{roundbpto}{2} \set:bp:rounder{roundbpto}{3} \set:bp:rounder{roundbpto}{4} \def\roundbpto#1{\csname roundbpto:#1\endcsname} \def\enablebpround{\let\tobp\roundbp} \def\disablebpround{\let\tobp\asbp} \def\setbpround#1{\expandafter\let\expandafter\tobp\csname roundbpto:\the\numexpr#1\relax\endcsname} % By default, \tobp respects \pdfdecimaldigits \enablebpround % save rounded to macro \def\savebp#1{% \def\s@vebp{% \edef#1{\tobp{\bp:dim@n}}}% \afterassignment\s@vebp\bp:dim@n} \newdimen\bp:dim@n % Lets play with basic arithmetic operations. To make things consistent, each % function expands to \numexpr|\dimexpr, even if could be easily expanded to % digits. This approach ensures predictable behaviour whenever a \function is % followed by \expandafter or \relax. To avoid evaluating the same expressions % twice or more, each function expands its parameters before performing the % final operation. This scheme makes temporary macros reusable. % absolute value \def\absint{\expandnumexprafter\absoluteint} \def\absoluteint#1{\numexpr\ifnum#1<\z@-\fi#1} \def\absdim{\expanddimexprafter\absolutedim} \def\absolutedim#1{\dimexpr\ifdim#1<\z@-\fi#1} % Various approaches to integer division: % floor(a/b) -- the largest integer LOWER than a/b % ceil(a/b) -- the lowest integer HIGHER than a/b % int(a/b) -- the integer part (floor for a/b>=0 and ceil for a/b<0) % nint(a/b) -- rounding (the nearest integer) \def\expanddivisionafter#1#2#3{% \expandnumexprafter#1{#2/#3}{#2}{#3}} \def\divfloor{\expandtwonumexprafter\dividefloor} \def\dividefloor{\expanddivisionafter\divide:fl@@r} \def\divide:fl@@r#1#2#3{% \numexpr#1% \ifcase\ifnum#2<0 \ifnum#3<0 1 \else 0 \fi \else \ifnum#3<0 1 \else 0 \fi \fi \ifnum\numexpr#1*#3>#2-\@ne\fi\or \ifnum\numexpr#1*#3<#2-\@ne\fi\fi} \def\divceil{\expandtwonumexprafter\divideceil} \def\divideceil{\expanddivisionafter\divide:c@il} \def\divide:c@il#1#2#3{% \numexpr#1% \ifcase\ifnum#2<0 \ifnum#3<0 1 \else 0 \fi \else \ifnum#3<0 1 \else 0 \fi \fi \ifnum\numexpr#1*#3<#2+\@ne\fi\or \ifnum\numexpr#1*#3>#2+\@ne\fi\fi} \def\divint{\expandtwonumexprafter\divideint} \def\divideint{\expanddivisionafter\divide:int} \def\divide:int#1#2#3{% \numexpr#1% \ifcase\ifnum#2<0 \ifnum#3<0 3 \else 1 \fi \else \ifnum#3<0 2 \else 0 \fi \fi \ifnum\numexpr#1*#3>#2-\@ne\fi\or \ifnum\numexpr#1*#3<#2+\@ne\fi\or \ifnum\numexpr#1*#3>#2+\@ne\fi\or \ifnum\numexpr#1*#3<#2-\@ne\fi\fi} \def\divnint{\expandtwonumexprafter\dividenint} \def\dividenint#1#2{\numexpr#1/#2} % modulo \def\mod{\expandtwonumexprafter\modulo} \def\modulo{\expanddivisionafter\do:m@dulo} \def\do:m@dulo#1#2#3{\numexpr#2-#3*\divide:fl@@r{#1}{#2}{#3}\relax} % If we don't divide (by) negative numbers, the following macros works % a bit faster. \def\divfloorpos{\expandtwonumexprafter\dividefloorpos} \def\dividefloorpos{\expanddivisionafter\divide:fl@@r:pos} \def\divide:fl@@r:pos#1#2#3{\numexpr#1\ifnum\numexpr#1*#3>#2-\@ne\fi} \def\divceilpos{\expandtwonumexprafter\divideceilpos} \def\divideceilpos{\expanddivisionafter\divide:c@il:pos} \def\divide:c@il:pos#1#2#3{\numexpr#1\ifnum\numexpr#1*#3<#2+\@ne\fi} \let\divintpos\divfloorpos \let\divideintegerpos\dividefloorpos \def\modpos{\expandtwonumexprafter\modulopos} \def\modulopos{\expanddivisionafter\modulo:p@s} \def\modulo:p@s#1#2#3{\numexpr#2-#3*\divide:fl@@r:pos{#1}{#2}{#3}\relax} % One sticky problem in all the division related macros above... We always % check if some rounding is present using the formula % \divident/\divisor*\divisor <=> \divident % It works fine if one say (2\maxdimen)/\maxdimen, but arithmetic overflow % occurs for (2\maxdimen)/(\maxdimen+1) or (2\maxdimen)/2. Thus, can be used % for reasonably small numbers. % Having an integer division we can round float-like strings \def\floatround#1{\divnint{\dimexpr#1pt}\p@} \def\floatfloor#1{\divfloor{\dimexpr#1pt}\p@} \def\floatceil#1{\divceil{\dimexpr#1pt}\p@} \def\floatint#1{\divint{\dimexpr#1pt}\p@} \def\floatnint#1{\divnint{\dimexpr#1pt}\p@} % Now lets implement integer by integer division with float-like result. The % following approach is quite fast and precise enough for most practical % purposes, but resulting floats are limited to \maxdimen expressed in points % (16383.99998). \def\fdivide{\expandtwonumexprafter\flo@t:divide} \def\flo@t:divide#1#2{\withoutpt\the\dimexpr\numexpr#1*\p@/#2\relax sp\relax} % Note, that \fdivide produce a float-like string that can be used as a factor % preceding a dimen (i.e \hsize=\fdivide{2}{7}\hsize). For sake of preciseness % however, internally we always use \dimexpr\hsize*2/7 in such cases (see % pdftrans.tex). % Yet another approach to division with float-like result. \divfloat produce a % float-like string with fixed precision. There is still numeric overflow risk % mentioned above. No preciseness and result limitation, however. \def\divfloat{% \expandthreenumexprafter\dividefloat} \def\dividefloat#1#2#3{% \expandnumberafter\divide:flo@t % \absoluteint returns \numexpr {\absoluteint{\divideint{#1}{#2}}}{#1}{#2}{#3}} \def\divide:flo@t#1#2#3{% it is not enough to check the sign of #1 \ifnum#2<0 \ifnum#3>0 -\fi\else \ifnum#2>0 \ifnum#3<0 -\fi\fi\fi #1.\expandthreenumexprafter\divide:fl@@t {#1}{\absoluteint{#2}}{\absoluteint{#3}}} \def\divide:fl@@t#1#2#3{% \expandnumexprafter\divide:flo@t:modulo{#2-#1*#3}{#3}} \def\divide:flo@t:modulo#1#2{% \ifnum#1<214748365 \expandtwonumbersafter\divide:flo@t:result{#10}{#2\expandafter}% \else \expandtwonumexprafter\divide:flo@t:modulo{#1/2}{#2/2\expandafter}% \fi} \def\divide:flo@t:result#1#2#3{% \ifnum#3>1 \expandtwonumexprafter\divide:flo@t:repeat {#3-\@ne}{\dividefloorpos{#1}{#2}\expandafter}% \else \number\divide:flo@t:last{#1}{#2}\relax \expandafter\gobbletwo \fi{#1}{#2}} \def\divide:flo@t:repeat#1#2#3#4{% #2\expandnumexprafter\divide:flo@t:modulo{#3-#2*#4}{#4}{#1}} \newcount\floatprecision \floatprecision=6 \def\roundlast{\let\divide:flo@t:last\dividenint} \def\floorlast{\let\divide:flo@t:last\divideint} \roundlast % One may say \let\tobp\roundfixedbp to enable alternative (fixed) rounding. \def\tofixedbp#1{\divfloat{\dimexpr#1}\b@\floatprecision} \def\roundfixedbp#1{\divfloat{\dimexpr#1}\b@\pdfdecimaldigits} % And now comes a real challenge -- trigonometry. In the first approach, values % of trigonometric functions were predefined for angles of range 0..90. Real % angle values were not supported. Now things are a bit slower, but much more % precise. In particular, we handle real angles values. The trigonometry % implementation is excerpted from trans.tex and originally was inspired by % mf.web. The clue is that any angle value (not only integer) can be % represented as a series of predefined ,,coins''. Trigonometric functions can % be then recursively calculated as follows: % % sin(a+b) = sin(a)cos(b) + sin(b)cos(a) % cos(a+b) = cos(a)cos(b) - sin(a)sin(b) % % Big thanks for BOP team for encourage. \def\fractdegree#1{\numexpr16*\dimexpr#1pt} % 2^20sp = 16pt = 1degree \edef\fractfactor{\number\numexpr\maxdimen+\@ne} % 2^30 \edef\fractfourth{\number\numexpr90*\fractdegree\@ne} % 90 degrees \edef\fractperiod{\number\numexpr4*\fractfourth} % 360 degrees \def\reducefractangle#1{% reduce to 0..#2 \expandnumberafter\reduce:fr@ct:angle{\fractdegree{#1}}} \def\reduce:fr@ct:angle#1#2{% \ifnum#1<0 \numexpr#2-\modpos{-#1}{#2}\relax \else \modpos{#1}{#2}% \fi} % For sake of backward compatibility we leave a hook for integer angles \def\reduceintangle#1#2{% \expandtwonumexprafter\reduce:int:@ngle{#1}{#2/\fractdegree\@ne}} \def\reduce:int:@ngle#1#2{\fractdegree{\reduce:fr@ct:angle{#1}{#2}}} \def\enablefractangle{\let\reducetrigangle\reducefractangle} \def\disablefractangle{\let\reducetrigangle\reduceintangle} \enablefractangle % some macro shortcuts \def\fracttrigfourth#1{% returns 0..3 (quarter) \dividefloorpos{#1}\fractfourth} \def\fr@ct:mul#1#2{#1*#2/\fractfactor} \def\fr@ct:div#1{\fdivide{#1}\fractfactor} % constant fractions \def\fr@ct:angle#1{\ifcase\numexpr#1\relax 62914560\or % 60 47185920\or % 45 31457280\or % 30 16777216\or % 2^4 8388608\or % 2^3 4194304\or % 2^2 2097152\or % 2^1 1048576\or % 2^0 524288\or % 2^-1 262144\or % 2^-2 131072\or % 2^-3 65536\or % 2^-4 32768\or % 2^-5 16384\or % 2^-6 8192\or % 2^-7 4096\or % 2^-8 2048\or % 2^-9 1024\or % 2^-10 512\or % 2^-11 256\or % 2^-12 128\or % 2^-13 64\or % 2^-14 32\or % 2^-15 16\or % 2^-16 8\or % 2^-17 4\or % 2^-18 2\or % 2^-19 1\fi}% 2^-20 \def\fr@ct:sin#1{\ifcase\numexpr#1\relax 929887697\or % 60 759250125\or % 45 536870912\or % 30 295963357\or % 2^4 149435979\or % 2^3 74900443\or % 2^2 37473049\or % 2^1 18739379\or % 2^0 9370046\or % 2^-1 4685068\or % 2^-2 2342539\or % 2^-3 1171270\or % 2^-4 585635\or % 2^-5 292818\or % 2^-6 146409\or % 2^-7 73204\or % 2^-8 36602\or % 2^-9 18301\or % 2^-10 9151\or % 2^-11 4575\or % 2^-12 2288\or % 2^-13 1144\or % 2^-14 572\or % 2^-15 286\or % 2^-16 143\or % 2^-17 71\or % 2^-18 36\or % 2^-19 18\fi}% 2^-20 \def\fr@ct:cos#1{\ifcase\numexpr#1\relax 536870912\or % 60 759250125\or % 45 929887697\or % 30 1032146887\or % 2^4 1063292242\or % 2^3 1071126243\or % 2^2 1073087729\or % 2^1 1073578288\or % 2^0 1073700939\or % 2^-1 1073731603\or % 2^-2 1073739269\or % 2^-3 1073741185\or % 2^-4 1073741664\or % 2^-5 1073741784\or % 2^-6 1073741814\or % 2^-7 1073741822\or % 2^-8 1073741823\or % 2^-9 1073741824\or % 2^-10 1073741824\or % 2^-11 1073741824\or % 2^-12 1073741824\or % 2^-13 1073741824\or % 2^-14 1073741824\or % 2^-15 1073741824\or % 2^-16 1073741824\or % 2^-17 1073741824\or % 2^-18 1073741824\or % 2^-19 1073741824\fi}% 2^-20 % I like \ifcase...\or...\fi acting as arrays because of its readability. % For sake of speed however, we say: \trans:count=0 \loop \expandafter\edef\csname fractangle:\the\trans:count\endcsname{\fr@ct:angle\trans:count} \expandafter\edef\csname fractsinvalue:\the\trans:count\endcsname{\fr@ct:sin\trans:count} \expandafter\edef\csname fractcosvalue:\the\trans:count\endcsname{\fr@ct:cos\trans:count} \ifnum\trans:count<27 \advance\trans:count by1 \repeat \def\fr@ct:angle#1{\csname fractangle:\number#1\endcsname} \def\fr@ct:sin#1{\csname fractsinvalue:\number#1\endcsname} \def\fr@ct:cos#1{\csname fractcosvalue:\number#1\endcsname} % Here the main loop starts. \def\fracttrig#1{% -> \expandnumexprafter\fr@ct:trig{\reducetrigangle{#1}\fractperiod}} \def\fr@ct:trig#1{% \csname fr@ct:trig:\romannumeral\fracttrigfourth{#1}+\@ne\endcsname {#1}} \def\fr@ct:trig:i#1#2#3{% \expandthreenumexprafter\fr@ct:trig:cont{#1}{#2}{#3}\z@} \def\fr@ct:trig:ii#1#2#3{% \expandthreenumexprafter\fr@ct:trig:cont{#1-\fractfourth}{#3}{-#2}\z@} \def\fr@ct:trig:iii#1#2#3{% \expandthreenumexprafter\fr@ct:trig:cont{#1-2*\fractfourth}{-#2}{-#3}\z@} \def\fr@ct:trig:iv#1#2#3{% \expandthreenumexprafter\fr@ct:trig:cont{#1-3*\fractfourth}{-#3}{#2}\z@} \def\fr@ct:trig:cont#1#2#3#4{% \ifcase \ifnum#1>0 \ifnum#1<\fr@ct:angle{#4} 0 \else 1 \fi \else 2 \fi \expandafter\fr@ct:trig:cont\expandafter {\number#1\expandafter}\expandafter {\number#2\expandafter}\expandafter {\number#3\expandafter}\expandafter {\number\numexpr#4+\@ne\expandafter}\or \expandafter\fr@ct:trig:cont\expandafter {\number\numexpr#1-\fr@ct:angle{#4}\expandafter}\expandafter {\number\numexpr\fr@ct:mul{#2}{\fr@ct:cos{#4}}% +\fr@ct:mul{#3}{\fr@ct:sin{#4}}\expandafter}\expandafter {\number\numexpr\fr@ct:mul{#3}{\fr@ct:cos{#4}}% -\fr@ct:mul{#2}{\fr@ct:sin{#4}}\expandafter}\expandafter {\number\numexpr#4+\@ne\expandafter}\or \fracttrigend{#2}{#3}\fi} % ...and finally, execute the next command with two first parameters being % scaled sine and cosine. \def\fracttrigend#1#2\fi#3{\fi#3{#1}{#2}} \def\fractsincos#1#2#3{\fracttrig{#3}\z@\fractfactor\fr@ct:sin:cos#1#2} \def\fr@ct:sin:cos#1#2#3#4{\def#3{#1}\def#4{#2}} % In pdftrans we use the following shortcuts \def\fr@ct:sin:cos:i#1#2#3{\fr@ct:trig:i{#3}\z@\fractfactor\fr@ct:sin:cos#1#2} \def\fr@ct:sin:cos:ii#1#2#3{\fr@ct:trig:ii{#3}\z@\fractfactor\fr@ct:sin:cos#1#2} \def\fr@ct:sin:cos:iii#1#2#3{\fr@ct:trig:iii{#3}\z@\fractfactor\fr@ct:sin:cos#1#2} \def\fr@ct:sin:cos:iv#1#2#3{\fr@ct:trig:iv{#3}\z@\fractfactor\fr@ct:sin:cos#1#2} \def\floatsincos#1#2#3{\fracttrig{#3}\z@\fractfactor\flo@t:sin:c@s#1#2} \def\flo@t:sin:c@s#1#2#3#4{% \edef#3{\fr@ct:div{#1}}% \edef#4{\fr@ct:div{#2}}} \endtrans \endinput