\documentclass[svgnames]{report} \usepackage{smartunits} \usepackage{manfnt} \usepackage{enumitem} \setlist[description]{font=\sffamily\bfseries\color{DodgerBlue},labelwidth=\textwidth} \usepackage{booktabs} \usepackage[a4paper,margin=18mm]{geometry} \synctex=1 \usepackage{fixltx2e} % LaTeX patches, \textsubscript \usepackage{cmap} % fix search and cut-and-paste in Acrobat \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \setcounter{secnumdepth}{0} \usepackage{listings}\lstset{language=[LaTeX]TeX,framerule=2pt} \lstset{language=[LaTeX]TeX, texcsstyle=*\bfseries\color{Blue}, backgroundcolor=\color{Ivory}, numbers=none, breaklines=true, keywordstyle=\color{Blue}, commentstyle=\color{Brown}, tabsize=2, morekeywords={SmartUnit,SmartUnitSettings,sisetup}, resetmargins=true, } % hyperref links to ctan \newcommand\ctan[1]{\href{https://www.ctan.org/pkg/#1}{\texttt{#1}}} \usepackage{xparse} \NewDocumentCommand{\Verb}{v}{{\color{blue}#1}} \newcommand\Section[1]{\subsection{\textcolor{blue}{#1}}} \makeatletter \author{Andrew Mathas} \usepackage{tikz} \usetikzlibrary{shadows.blur} \tikzset{shadowed/.style={blur shadow={shadow blur steps=5}, bottom color=LightSkyBlue!30, draw=MidnightBlue!70,shade, font=\normalfont\Huge\bfseries\scshape, rounded corners=8pt, top color=SkyBlue, }, boxes/.style={draw=MidnightBlue, fill=Cornsilk, font=\sffamily\small, inner sep=5pt, rectangle, rounded corners=8pt, text=RoyalBlue, } } \def\SmartTitle{ \begin{tikzpicture}[remember picture,overlay] \node[yshift=-3cm] at (current page.north west) {\begin{tikzpicture}[remember picture, overlay] \draw[shadowed](30mm,0) rectangle node[white]{Smart Units} (\paperwidth-30mm,16mm); \node[anchor=west,boxes] at (4cm,0cm) {\@author}; \node[anchor=east,boxes] at (\paperwidth-4cm,0) {\smart@version}; \end{tikzpicture} }; \end{tikzpicture} \vspace*{20mm} } \def\@oddfoot{\textsc{Smart Units} -- \smart@version\hfill\thepage} \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} \hypersetup{pdfcreator={ Generated by pdfLaTeX }, pdfinfo={Author ={ Andrew Mathas }, Keywords={ units, metric, Imperial }, License ={ LaTeX Project Public License v1.3c or later }, Subject ={ Convert between metric and Imperial units }, Title ={ smartunits - \smart@version } }, } \makeatother \begin{document} \SmartTitle %\Section{Smart units} This package implements a \Verb|\SmartUnit| macro for converting between the following metric and Imperial units: \begin{center} \begin{tabular}{@{\color{Grey!50}}ll@{\quad$\longleftrightarrow$\quad}l}\toprule & Metric & Imperial \\\midrule \color{LightSkyBlue}distance & kilometers & miles \\ \color{LightSkyBlue}length & centimeters & feet and inches \\ \color{LightSkyBlue}temperature & Celsius & Fahrenheit \\ \color{LightSkyBlue}time & 24-hour time& 12-hour time \\ \color{LightSkyBlue}volume & litres & USA/UK gallons \\ \color{LightSkyBlue}weight & kilograms & pounds \\ \bottomrule \end{tabular} \end{center} Of course, the underlying unit for length and distance is the same but it makes sense to convert between units of similar ``scale''. The \Verb|\SmartUnit| macro is designed to print only one unit at a time, where the quantity is given in terms of either metric or Imperial units (there is no error checking, however). Depending on the (global) settings the unit will be printed either as a metric unit, or as an Imperial unit, or both. If the required unit is not given as an argument to \Verb|\SmartUnit| then it is computed. The units are printed using \ctan{siunitx}, so the formatting of the units can be controlled using \Verb|\sisetup| together with formatting parameters from \ctan{siunitx}. Some aspects of the formatting can be controlled ``locally'' using \Verb|\SmartUnit| and ``globally'' using \Verb|\SmartUnitSettings|. The \Verb|\SmartUnit| macro can print units in the following four different formats: \begin{lstlisting} \SmartUnit{metric} % --> metric \SmartUnit{imperial} % --> Imperial \SmartUnit{metric imperial} % --> metric (Imperial) \SmartUnit{imperial metric} % --> Imperial (metric) \end{lstlisting} For the impatient, here are some examples: \bgroup \sisetup{round-mode=places,round-precision=2} \small \begin{lstlisting}[escapeinside=<>] \documentclass{article} \usepackage{smartunits} \begin{document} \SmartUnitSettings{places=2} % Some global settings < \SmartUnitSettings{places=2} > \SmartUnit{metric imperial} % Output format: metric (Imperial) < \SmartUnit{metric imperial} > \SmartUnit{cm=10} % < \SmartUnit{cm=10} > \SmartUnit{cm=66} % < \SmartUnit{cm=66} > \SmartUnit{feet=4,inches=3} % < \SmartUnit{feet=4,inches=3} > \SmartUnit{celsius=20} % < \SmartUnit{celsius=20} > \SmartUnit{metric} % Output format: metric < \SmartUnit{metric} > \SmartUnit{miles=5, places=1} % < \SmartUnit{miles=5,places=1} > \SmartUnit{imperial} % Output format: Imperial < \SmartUnit{imperial} > \SmartUnit{hours=12, minutes=1} % < \SmartUnit{hours=12, minutes=1} > \SmartUnit{km=5,places=3} % < \SmartUnit{km=5,places=3} > \SmartUnit{imperial metric} % Output format: Imperial (metric) < \SmartUnit{imperial metric} > \SmartUnit{miles=5, km=7} % < \SmartUnit{miles=5,km=7} > \SmartUnit{l=1.0, places=1} % < \SmartUnit{l=1.0, places=1} > \SmartUnit{L=1.0, places=1} % < \SmartUnit{L=1.0, places=1} > \SmartUnit{gal=25.0,uk,places=2} % < \SmartUnit{gal=25.0, places=2,uk} > \SmartUnit{gal=25.0,usa,places=2} % < \SmartUnit{gal=25.0, places=2,usa} > \end{document} \end{lstlisting} \egroup The calculations done by \Verb|\SmartUnit| are reasonably accurate but LaTeX is not a calculator so rounding and other errors can and do happen (indeed, see the displayed example below). In addition, even assuming that the calculations done by \Verb|\SmartUnit| are accurate this may not be what you want. For example, if the metric unit is given as \SI{100}{km} then many people will want the Imperial unit to be written as \SI{60}{\text{mi}}, rather than the more accurate \SI{62.137119}{\text{mi}}. In fact, \Verb|\SmartUnit| can cater for these considerations because all units are printed using the \ctan{siunitx} package, which is really clever. The precision of the printed units can be controlled using \Verb|\sisetup|, from the \ctan{siunitx} package or by using the short-hands, \textsf{figures} and \textsf{places}, provided by \Verb|\SmartUnit|: \begin{lstlisting}[texcl] \SmartUnit{km=100.0, figures=1} % \SmartUnit{km=100.0,figures=1} \SmartUnit{km=100.0, places=5} % \SmartUnit{km=100.0,places=5} \end{lstlisting} \noindent (Comparing with the paragraph above, the last calculation is only accurate to three decimal places!) The values of the computed units can be overridden, during proof-reading for example, by specifying both the metric and Imperial units: \begin{lstlisting}[texcl] \SmartUnit{km=100,miles=70} % \SmartUnit{km=100,miles=70} \end{lstlisting} \noindent \raisebox{3.9mm}{\dbend}\quad Of course, if you specify units incorrectly like this then incorrect values will be printed. Behind the scenes all units are printed using \ctan{siunitx}. Therefore, the precision and formatting of the units can be changed using the options to \Verb|\sisetup| described in the \ctan{siunitx}\ manual. In particular, when the rounding capabilities of~\ctan{siunitx}\ are enabled any units calculated by \Verb|\SmartUnit| will be rounded. In this case there is a shorthand for overriding the global \ctan{siunitx}\ settings for the rounding precision on an individual ``smart unit'': \begin{lstlisting}[escapeinside=<>] % Locally=\sisetup{round-precision=3, round-mode=places} \SmartUnit{cm=2, places=3} % < \SmartUnit{cm=2, places=3} > \end{lstlisting} Section~5.5 of the \ctan{siunitx}\ manual should be consulted for all of the options for controlling ``post-processing'' of numbers. In particular, units that are given as integers will always be printed as integers. For example, the metric version of \Verb|\SmartUnit{cm=2, places=3}| is printed as \SmartUnit{cm=2, places=3, metric} whereas the Imperial version is \num{0.787}$'$. If, instead, we use \Verb|\SmartUnit{cm=2.0, places=3}| then the metric unit is printed as \SmartUnit{cm=2.0, places=3, metric}. \Section{Unit conversions} For all of these examples we use the following global settings: \begin{lstlisting}[texcl] \SmartUnitSettings{places=2, metric imperial} % Global settings \end{lstlisting} \SmartUnitSettings{places=2, metric imperial} \noindent Of course, as in some of the example below, this can always be overridden locally. \begin{description} \item[Distance -- km, miles, mi] Conversions between kilometers and miles. Miles can be specified using either \textbf{miles} or \textbf{mi}. \begin{lstlisting}[texcl] \SmartUnit{km=100,miles=60} % \SmartUnit{km=100,miles=60} \SmartUnit{km=100.0,figures=1} % \SmartUnit{km=100,figures=1} \SmartUnit{miles=62.15,places=1} % \SmartUnit{miles=62.15,places=1} \end{lstlisting} \item[Length -- cm, inches, feet] Feet are always printed as integers. Significant figures/places are applied only to the printing of inches. \begin{lstlisting}[texcl] \SmartUnit{feet=7, inches=1} % \SmartUnit{feet=7, inches=1} \SmartUnit{cm=189, feet=7, inches=1} % \SmartUnit{cm=189, feet=7, inches=1} \SmartUnit{cm=191, places=1} % \SmartUnit{cm=191, places=1} \end{lstlisting} \item[Temperature -- celsius, fahrenheit] Convesions between Celsius and Fehrenheit can be speficied using \textsf{celsius}, \textsf{C}, \textsf{fahrenheit} or \textsf{F}. \begin{lstlisting}[texcl] \SmartUnit{celsius=32,places=3} % \SmartUnit{celsius=32,places=3} \SmartUnit{celsius=0} % \SmartUnit{celsius=0} \SmartUnit{C=37} % \SmartUnit{C=37} \SmartUnit{fahrenheit=0} % \SmartUnit{fahrenheit=0,places=2} \SmartUnit{F=22,figures=4} % \SmartUnit{F=212,figures=4} \end{lstlisting} \item[Time -- hours, minutes, seconds, am, pm] \begin{lstlisting}[texcl] \SmartUnit{hours=0, minutes=59} % \SmartUnit{hours=0, minutes=59} \SmartUnit{hours=12, minutes=12} % \SmartUnit{hours=12, minutes=12} \SmartUnit{hours=13, minutes=9} % \SmartUnit{hours=13, minutes=9} \SmartUnit{hours=8, minutes=31, pm} % \SmartUnit{hours=8, minutes=31, pm} \SmartUnit{hours=9, minutes=3, am} % \SmartUnit{hours=9, minutes=3, am} \SmartUnit{hours=0, minutes=0,seconds=1} % \SmartUnit{hours=0, minutes=0,seconds=1} \SmartUnit{hours=12, minutes=0,seconds=1} % \SmartUnit{hours=12, minutes=0,seconds=1} \end{lstlisting} \item[Volume -- L, l, gal, gallons] Converting between litres and gallons. Liters can be specified using \textbf{L} or \textbf{l} and gallons can be given with \textbf{gallons} or \textbf{gal}. Both UK gallons and USA gallons are supported, with USA gallons being the default. \begin{lstlisting}[texcl] \SmartUnit{l=10.0, places=1} % \SmartUnit{l=10.0, places=1} \SmartUnit{L=10.0, places=1,uk} % \SmartUnit{L=10.0, places=1,uk} \SmartUnit{gal=10.0, places=2} % \SmartUnit{gal=10.0, places=2} \end{lstlisting} \item[Weight -- kg, pounds, lbs] \begin{lstlisting}[texcl] \SmartUnit{kg=10.0, places=1} % \SmartUnit{kg=10.0, places=1} \SmartUnit{pound=10.0, places=1} % \SmartUnit{pound=10.0, places=1} \SmartUnit{pound=10.0,figures=1} % \SmartUnit{pound=10.0,figures=1} \end{lstlisting} \end{description} \Section{Formatting options} The following options can either be used inside \Verb|\SmartUnit| to control the formatting \textit{locally} for just the unit being printed. They can also be used as arguments to \Verb|\SmartUnitSettings| to change the formatting of all subsequent smart units (unless these settings are overridden locally). More control over the formatting of the units is given by the \Verb|\sisetup| macro from the \ctan{siunitx} package. \begin{description} \item[figures] This is equivalent to \Verb|\sisetup{round-mode=figures, precision=#1}|. Units are printed with \#1 significant figures. \begin{lstlisting}[texcl] \SmartUnit{miles=60, metric, figures=0} % \SmartUnit{miles=60, metric, figures=0} \SmartUnit{miles=60, metric, figures=1} % \SmartUnit{miles=60, metric, figures=1} \SmartUnit{miles=60, metric, figures=2} % \SmartUnit{miles=60, metric, figures=2} \SmartUnit{miles=60, metric, figures=3} % \SmartUnit{miles=60, metric, figures=3} \SmartUnit{miles=60, metric, figures=4} % \SmartUnit{miles=60, metric, figures=4} \end{lstlisting} \item[places] This is equivalent to \Verb|\sisetup{round-mode=places, precision=#1}|. Units are printed with \#1 decimal places. \begin{lstlisting}[texcl] \SmartUnit{miles=60, metric, places=0} % \SmartUnit{miles=60, metric, places=0} \SmartUnit{miles=60, metric, places=1} % \SmartUnit{miles=60, metric, places=1} \SmartUnit{miles=60, metric, places=2} % \SmartUnit{miles=60, metric, places=2} \SmartUnit{miles=60, metric, places=3} % \SmartUnit{miles=60, metric, places=3} \SmartUnit{miles=60, metric, places=4} % \SmartUnit{miles=60, metric, places=4} \end{lstlisting} \item[imperial] Print only Imperial units. \begin{lstlisting}[texcl] \SmartUnit{km=1.0, imperial} % \SmartUnit{km=1.0, imperial, figures=2} \SmartUnit{miles=1.0, imperial} % \SmartUnit{miles=1.0, imperial, figures=2} \end{lstlisting} \item[imperial metric] Print Imperial units with metric units enclosed in brackets. \begin{lstlisting}[texcl] \SmartUnit{km=1.0, imperial metric} % \SmartUnit{km=1.0, imperial metric, figures=2} \SmartUnit{miles=1.0, imperial metric} % \SmartUnit{miles=1.0, imperial metric, figures=2} \end{lstlisting} \item[metric (default)] Print only metric units. \begin{lstlisting}[texcl] \SmartUnit{km=1.0, metric} % \SmartUnit{km=1.0, metric, figures=2} \SmartUnit{miles=1.0, metric} % \SmartUnit{miles=1.0, metric, figures=2} \end{lstlisting} \item[metric imperial] Print metric units with Imperial units enclosed in brackets. \begin{lstlisting}[texcl] \SmartUnit{km=1.0, metric imperial} % \SmartUnit{km=1.0, metric imperial, figures=2} \SmartUnit{miles=1.0, metric imperial} % \SmartUnit{miles=1.0, metric imperial, figures=2} \end{lstlisting} \item[uk] Selects the UK variants of the Imperial units (currently this only affects gallons): \begin{lstlisting}[texcl] \SmartUnit{l=1.0, imperial metric, figures=2,uk} % \SmartUnit{l=1.0, imperial metric, figures=2,uk} \SmartUnit{gal=1.0, imperial metric, figures=2,uk} % \SmartUnit{gal=1.0, imperial metric, figures=2,uk} \end{lstlisting} \item[usa (default)] Selects the USA variants of the Imperial units (currently this only affects gallons): \begin{lstlisting}[texcl] \SmartUnit{l=1.0, imperial metric, figures=2,usa} % \SmartUnit{l=1.0, imperial metric, figures=2,usa} \SmartUnit{gal=1.0, imperial metric, figures=2,usa} % \SmartUnit{gal=1.0, imperial metric, figures=2,usa} \end{lstlisting} By default \Verb|\SmartUnit| uses the American Imperial units. This is a purely democratic choice dictated by the populations of the two countries. \end{description} \Section{The code} Behind the scenes, the \Verb|\SmartUnit| macro uses \ctan{pgfkeys} and \ctan{pgfmath} to convert between the different units. The interface is surprisingly simple and easy to extend. For example, the following code takes care of the conversion between kilometers and miles: \bgroup\footnotesize \begin{lstlisting} %%%% convert=distance: km <-> miles %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% km/.style={convert=distance, unit km=#1},% mi/.style={convert=distance, unit miles=#1},% miles/.style={convert=distance, unit miles=#1},% distance/metric/.style={/SmartUnit/conversion={km}{miles}{0}{1.609344}{0}{km}},% distance/imperial/.style={/SmartUnit/conversion={miles}{km}{0}{0.62137119}{0}{\text{mi}}},% \end{lstlisting} \egroup \noindent Most of the conversions are done in this way --- the exceptions are ``length'' and ``time'', which are more complicated. The key \Verb|/SmartUnit/conversion| is a macro that takes six arguments and then implements the generic conversion formula: \[ \text{New unit}(\Verb|#1|) = \Big(\text{Old unit}(\Verb|#2)|+\text{Pre-offset}(\Verb|#3|)\Big)*\text{Multiplier}(\Verb|#4|) + \text{Post-offset}(\Verb|#5|), \] where \Verb|#6| is the metric or Imperial symbol for the unit being printed. \textit{Adding additional conversions is quite straightforward and I am happy to incorporate suggestions.} There is, of course, some trickery driving how the units are printed. Here is the definition of \Verb|\SmartUnit|: \begin{lstlisting} \newcommand\SmartUnit[1]{% \bgroup%=> changes are local to \SmartUnit => no need to reset \pgfkeys{/SmartUnit,#1}% Pass the keys to /SmartUnit \pgfkeys{/SmartUnit,printunit}% Calculate units and print \egroup% } \end{lstlisting} So \Verb|\SmartUnit| calls \ctan{pgfkeys} twice: the first time to set the keys and the second time to print them using \Verb|/SmartUnit/printunit|. In turn, this uses \Verb|/SmartUnit/convert| to identify the unit being printed, after which the pgf key \Verb|/SmartUnit/output| is called. This has subkeys for the four different \textit{output formats}: \begin{center} \texttt{metric}, \texttt{imperial}, \texttt{metric imperial} and \texttt{imperial metric}. \end{center} Finally, \Verb|/SmartUnit/output/| uses \Verb|/SmartUnit/convert| to perform the required conversion. This multi-step printing process is necessary for converting between inputs like time or centimeters, feet and inches, where the output units depend on more than one input value. It is also used to print the four different output formats discussed above. \Section{Acknowledgement} The package was written in response to a \href{http://tex.stackexchange.com/questions/241169/}{TeX.SX question}, partly as a proof-of-concept and partly as an exercise to learn how to use \ctan{pgfkeys}. Thanks to Mark Adams for asking the question. \Section{Author} \href{mailto:Andrew.Mathas@gmail.com?smartunits}{Andrew Mathas}, \copyright\space 2015, 2016. \Section{Licence} Released under the \href{http://www.latex-project.org/lppl.txt}% {LaTeX Project Public License}, v1.3c or later. \end{document}