% \iffalse meta-comment % %% File: xcoffins.dtx % % Copyright (C) 2010-2024 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % This file is part of the "l3experimental bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver> \documentclass[full]{l3doc} \usepackage{xcoffins} % Not yet part of expl3, so not in l3doc \usepackage{xcolor} % As coffins only loads basic color support \NewCoffin \ExampleCoffin \NewCoffin \SmallCoffin \NewCoffin \OutputCoffin \NewCoffin \RedCoffin \NewCoffin \BlueCoffin \NewCoffin \GreenCoffin \NewCoffin \YellowCoffin \NewCoffin \OrangeCoffin \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{xcoffins} package\\ Design-level coffins^^A % } % % \author{^^A % The \LaTeX{} Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2024-03-14} % % \maketitle % % \begin{documentation} % % \section{Introduction: the coffin concept} % % In \LaTeX3 terminology, a \enquote{coffin} is a box containing % typeset material. Along with the box itself, the coffin structure % includes information on the size and shape of the box, which makes % it possible to align two or more coffins easily. This is achieved % by providing a series of `poles' for each coffin. These % are horizontal and vertical lines through the coffin at defined % positions, for example the top or horizontal centre. The points % where these poles intersect are called \enquote{handles}. Two % coffins can then be aligned by describing the relationship between % a handle on one coffin with a handle on the second. In words, an % example might then read % \begin{quote} % Align the top-left handle of coffin A with the bottom-right % handle of coffin B. % \end{quote} % % The locations of coffin handles are much easier to understand % visually. Figure~\ref{fgr:handles} shows the standard handle % positions for a coffin typeset in horizontal mode (left) and in % vertical mode (right). Notice that the later case results in a greater % number of handles being available. As illustrated, each handle % results from the intersection of two poles. For example, the centre % of the coffin is marked |(hc,vc)|, \emph{i.e.}~it is the % point of intersection of the horizontal centre pole with the % vertical centre pole. New handles are generated automatically when % poles are added to a coffin: handles are \enquote{dynamic} entities. % % \begin{figure} % \hfil % \begin{minipage}{0.4\textwidth} % \SetHorizontalCoffin\ExampleCoffin % {\color{black!10!white}\rule{1 in}{1 in}} % \DisplayCoffinHandles\ExampleCoffin{blue} % \end{minipage} % \hfil % \begin{minipage}{0.4\textwidth} % \SetVerticalCoffin\ExampleCoffin{1 in} % {\color{black!10!white}\rule{1 in}{1 in}} % \DisplayCoffinHandles\ExampleCoffin{blue} % \end{minipage} % \hfil % \caption{Standard coffin handles: left, horizontal coffin; right, % vertical coffin} % \label{fgr:handles} % \end{figure} % %\section{Creating and setting coffins} % % Before any alignment can take place, coffins must be created and % their contents must be created. All coffin operations are local % to the current \TeX{} group with the exception of coffin creation. % \begin{function}{\NewCoffin} % \begin{syntax} % \cs{NewCoffin} \meta{coffin} % \end{syntax} % Before a \meta{coffin} can be used, it must be allocated using % \cs{NewCoffin}. The name of the \meta{coffin} should be a % control sequence (starting with the escape character, usually % |\|), for example % \begin{verbatim} % \NewCoffin\MyCoffin % \end{verbatim} % Coffins are allocated globally, and an error will be raised if the % name of the \meta{coffin} is not globally-unique. % \end{function} % % \begin{function}{\SetHorizontalCoffin} % \begin{syntax} % \cs{SetHorizontalCoffin} \meta{coffin} \Arg{material} % \end{syntax} % Typesets the \meta{material} in horizontal mode, storing the result % in the \meta{coffin}. The standard poles for the \meta{coffin} are % then set up based on the size of the typeset material. % \end{function} % % \begin{function}{\SetVerticalCoffin} % \begin{syntax} % \cs{SetVerticalCoffin} \meta{coffin} \Arg{width} \Arg{material} % \end{syntax} % Typesets the \meta{material} in vertical mode constrained to the % given \meta{width} and stores the result in the \meta{coffin}. The % standard poles for the \meta{coffin} are then set up based on the % size of the typeset material. % \end{function} % % \section{Controlling coffin poles} % % A number of standard poles are automatically generated when the coffin % is set or an alignment takes place. The standard poles for all coffins % are: % \begin{itemize}[font = \ttfamily] % \item[l] a pole running along the left-hand edge of the bounding % box of the coffin; % \item[hc] a pole running vertically through the centre of the coffin % half-way between the left- and right-hand edges of the bounding % box (\emph{i.e.}~the \enquote{horizontal centre}); % \item[r] a pole running along the right-hand edge of the bounding % box of the coffin; % \item[b] a pole running along the bottom edge of the bounding % box of the coffin; % \item[vc] a pole running horizontally through the centre of the % coffin half-way between the bottom and top edges of the bounding % box (\emph{i.e.}~the \enquote{vertical centre}); % \item[t] a pole running along the top edge of the bounding % box of the coffin; % \item[H] a pole running along the baseline of the typeset material % contained in the coffin. % \end{itemize} % In addition, coffins containing vertical-mode material also % feature poles which reflect the richer nature of these systems: % \begin{itemize} % \item[B] a pole running along the baseline of the material at the % bottom of the coffin. % \item[T] a pole running along the baseline of the material at the top % of the coffin. % \end{itemize} % % \begin{function}{\SetHorizontalPole} % \begin{syntax} % \cs{SetHorizontalPole} \meta{coffin} \Arg{pole} \Arg{offset} % \end{syntax} % Sets the \meta{pole} to run horizontally through the \meta{coffin}. % The \meta{pole} will be located at the \meta{offset} from the % baseline of the \meta{coffin}. The % \meta{offset} should be given as a dimension expression; this may % include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and % \cs{Width}, which will evaluate to the appropriate dimensions of % the \meta{coffin}. For example, to create a pole running % horizontally through the coffin at one third of the distance from % the base of the coffin to the top, the appropriate instruction would % be % \begin{verbatim} % \SetHorizontalPole \MyCoffin {height/3} {\TotalHeight/3} % \end{verbatim} % Note that poles which run \emph{horizontally} are described in terms % of their \emph{vertical} location in the coffin. Also notice that % the total height of the coffin is described by the sum of % \cs{Height} and \cs{Depth}: these are both measured from the % horizontal baseline of the material in the coffin. % \end{function} % % \begin{function}{\SetVerticalPole} % \begin{syntax} % \cs{SetVerticalPole} \meta{coffin} \Arg{pole} \Arg{offset} % \end{syntax} % Sets the \meta{pole} to run vertically through the \meta{coffin}. % The \meta{pole} will be located at the \meta{offset} from the % left-hand edge of the bounding box of the \meta{coffin}. The % \meta{offset} should be given as a dimension expression; this may % include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and % \cs{Width}, which will evaluate to the appropriate dimensions of % the \meta{coffin}. For example, to create a pole running vertically % through the coffin at one third of the distance from the left-hand % edge, the appropriate instruction would be % \begin{verbatim} % \SetVerticalPole \MyCoffin {width/3} {\Width/3} % \end{verbatim} % Note that poles which run \emph{vertically} are described in terms % of their \emph{horizontal} location in the coffin. % \end{function} % % \begin{function}{\TotalHeight} % \begin{syntax} % \cs{TotalHeight} % \end{syntax} % Within the \meta{offset} argument of \cs{SetHorizontalPole} and % \cs{SetVerticalPole}, \cs{TotalHeight} will give the distance from % the base to the top of the bounding box of the relevant coffin. % \end{function} % % \begin{function}{\Height} % \begin{syntax} % \cs{Height} % \end{syntax} % Within the \meta{offset} argument of \cs{SetHorizontalPole} and % \cs{SetVerticalPole}, \cs{Height} will give the distance from the % baseline to the top of the bounding box of the relevant coffin. % \end{function} % % \begin{function}{\Depth} % \begin{syntax} % \cs{Depth} % \end{syntax} % Within the \meta{offset} argument of \cs{SetHorizontalPole} and % \cs{SetVerticalPole}, \cs{Depth} will give the distance from the % baseline to the bottom of the bounding box of the relevant coffin. % \end{function} % % \begin{function}{\Width} % \begin{syntax} % \cs{Width} % \end{syntax} % Within the \meta{offset} argument of \cs{SetHorizontalPole} and % \cs{SetVerticalPole}, \cs{Width} will give the distance from the % right edge to the left edge of the bounding box of the relevant % coffin. % \end{function} % % \section{Rotating coffins} % % \begin{function}{\RotateCoffin} % \begin{syntax} % \cs{RotateCoffin} \meta{coffin} \Arg{angle} % \end{syntax} % Rotates the \meta{coffin} by the given \meta{angle} about its reference % point (given in degrees counter-clockwise) . This process will rotate both % the coffin content and poles. Multiple rotations will not result in % the bounding box of the coffin growing unnecessarily. % % The effect of rotation on a coffin is illustrated in % Figure~\ref{fgr:rotation}. As is shown, the coffin handles will % remain correctly positioned relative to the content of the coffin. % The \enquote{top} of a rotated coffin may of course no longer be the % edge closest to the top of the physical page. % \end{function} % % \begin{figure} % \hfil % \SetHorizontalCoffin\ExampleCoffin % {^^A % \color{black!10!white}\rule{0.5 in}{1 in}^^A % \color{black!20!white}\rule{0.5 in}{1 in}^^A % } % \begin{minipage}{0.4\textwidth} % \DisplayCoffinHandles\ExampleCoffin{blue} % \end{minipage} % \hfil % \begin{minipage}{0.4\textwidth} % \RotateCoffin\ExampleCoffin{45} % \DisplayCoffinHandles\ExampleCoffin{blue} % \end{minipage} % \hfil % \caption{Coffin rotation: left, unrotated; right, rotated by % $45$\textdegree.} % \label{fgr:rotation} % \end{figure} % % \section{Resizing coffins} % % \begin{function}{\ResizeCoffin} % \begin{syntax} % \cs{ResizeCoffin} \meta{coffin} \Arg{width} \Arg{total-height} % \end{syntax} % Resized the \meta{coffin} to \meta{width} and \meta{total-height}, % both of which should be given as dimension expressions. % \end{function} % % \begin{function}{\ScaleCoffin} % \begin{syntax} % \cs{ScaleCoffin} \meta{coffin} \Arg{x-scale} \Arg{y-scale} % \end{syntax} % Scales the \meta{coffin} by a factors \meta{x-scale} and % \meta{y-scale} in the horizontal and vertical directions, % respectively. The two scale factors should be given as real numbers. % \end{function} % % \cs{ResizeCoffin} and \cs{ScaleCoffin} can be used interchangeably: % whether scale factors or absolute values are the best form for the % resizing will depend upon the context (Figure~\ref{fgr:resizing}). % % \begin{figure} % \hfil % \SetHorizontalCoffin\ExampleCoffin % {^^A % \color{black!10!white}\rule{0.5 in}{1 in}^^A % \color{black!20!white}\rule{0.5 in}{1 in}^^A % } % \begin{minipage}{0.4\textwidth} % \ResizeCoffin \ExampleCoffin {4 cm} {3 cm} % \DisplayCoffinHandles \ExampleCoffin {blue} % \end{minipage} % \hfil % \begin{minipage}{0.4\textwidth} % \ScaleCoffin \ExampleCoffin {2.0} {0.5} % \DisplayCoffinHandles \ExampleCoffin {blue} % \end{minipage} % \hfil % \caption{Coffin resizing: left, resized to exactly $4$\,cm by % $6$\,cm; right, scaled a factors of $2$ and $0.5$ in % $x$ and $y$, respectively (example coffin as in % Figure~\ref{fgr:rotation}).} % \label{fgr:resizing} % \end{figure} % % \section{Joining coffins} % % The key operation for coffins is joining coffins to each other. This % is always carried out such that the first coffin is the % \enquote{parent}, and is updated by the alignment. The second % \enquote{child} coffin is not altered by the alignment process. % % \begin{function}{\JoinCoffins} % \begin{syntax} % \cs{JoinCoffins} * % ~~\meta{coffin1} [ \meta{coffin1-pole1} , \meta{coffin1-pole2} ] % ~~\meta{coffin2} [ \meta{coffin2-pole1} , \meta{coffin2-pole2} ] % ~~( \meta{x-offset} , \meta{y-offset} ) % \end{syntax} % Joining of two coffins is carried out by the \cs{JoinCoffins} % function, which takes two mandatory arguments: the \enquote{parent} % \meta{coffin1} and the \enquote{child} \meta{coffin2}. All of the % other arguments shown are optional. % \end{function} % % The standard \cs{JoinCoffins} functions joins \meta{coffin2} to % \meta{coffin1} such that the bounding box of \meta{coffin1} after the % process will expand. The new bounding box will be the smallest % rectangle covering the bounding boxes of the two input coffins. % When the starred variant of \cs{JoinCoffins} is used, the bounding % box of \meta{coffin1} is not altered, \emph{i.e.}~\meta{coffin2} may % protrude outside of the bounding box of the updated \meta{coffin1}. % The difference between the two forms of alignment is best illustrated % using a visual example. In Figure~\ref{fgr:alignment}, the two % processes are contrasted. In both cases, the small red coffin has been % aligned with the large grey coffin. In the left-hand illustration, % the \cs{JoinCoffins} function was used, resulting in an expanded % bounding box. In contrast, on the right \cs{AttachCoffin} was used, % meaning that the bounding box does not include the area of the % smaller coffin. % % \begin{figure} % \fboxsep 0 pt\relax % \SetHorizontalCoffin\ExampleCoffin % {\color{black!20!white}\rule{1 in}{1 in}} % \SetHorizontalCoffin \SmallCoffin % {\color{red!20!white}\rule{0.1 in}{0.1 in}} % \hfil % \begin{minipage}{0.4\textwidth} % \centering % \JoinCoffins\ExampleCoffin[vc,r]\SmallCoffin[vc,l] % \fbox{\TypesetCoffin\ExampleCoffin} % \end{minipage} % \hfil % \begin{minipage}{0.4\textwidth} % \centering % \JoinCoffins*\ExampleCoffin[vc,r]\SmallCoffin[vc,l] % \fbox{\TypesetCoffin\ExampleCoffin}% % \end{minipage} % \hfil % \caption{Contrast between \cs{JoinCoffins} (left) and % \cs{JoinCoffins*} (right); the bounding box of the coffin is show % in black.} % \label{fgr:alignment} % \end{figure} % % The alignment is carried out by first calculating \meta{handle1}, the % point of intersection of \meta{coffin1-pole1} and % \meta{coffin1-pole2}, and \meta{handle2}, the point of intersection % of \meta{coffin2-pole1} and \meta{coffin2-pole2}. If the two % \meta{poles} are not specified, \cs{JoinCoffins} will use the % default value |(H,l)|, \emph{i.e.}~the reference point used by \TeX\ % for the underlying box. Once the two \meta{handles} have been % located, \meta{coffin2} is then attached to \meta{coffin1} such that % the relationship between \meta{handle1} and \meta{handle2} is % described by the \meta{x-offset} and \meta{y-offset}. This % \meta{offset} is an optional argument, and if it is not given then % |(0 pt, 0 pt)| is used. % % Notice that when \cs{JoinCoffins} is used the new bounding box is % the smallest rectangle containing the bounding boxes of the two input % coffins. As a result, it will include additional white space unless % one coffin entirely overlaps the other (Figure~\ref{fgr:bounding}, % left). Rotation of coffins will take account of the extent of the % material after rotation when re-calculating the bounding box. This % means that no \emph{unnecessary} white space will be added on % rotation (Figure~\ref{fgr:bounding}, right). % % \begin{figure} % \fboxsep 0 pt\relax % \SetHorizontalCoffin\ExampleCoffin % {\color{black!20!white}\rule{1 in}{1 in}} % \SetHorizontalCoffin\SmallCoffin % {\color{red!20!white}\rule{0.1 in}{0.1 in}} % \JoinCoffins\ExampleCoffin[vc,r]\SmallCoffin[vc,l] % \hfil % \begin{minipage}{0.4\textwidth} % \centering % \fbox{\copy\ExampleCoffin} % \end{minipage} % \hfil % \begin{minipage}{0.4\textwidth} % \centering % \RotateCoffin\ExampleCoffin{135} % \fbox{\copy\ExampleCoffin} % \end{minipage} % \hfil % \caption{The effect of rotation of a joined coffin: the black line % shows the coffin bounding box.} % \label{fgr:bounding} % \end{figure} % % As part of the joining procedure, the poles of the two input coffins % are preserved within the structure of the updated coffin. In this way % it is possible to carry out complex alignment procedures. The poles of % a coffin after alignment may therefore be divided into three groups: % \begin{enumerate} % \item The \enquote{native} poles of the updated coffin, such as % \texttt{l}, \texttt{r}, \texttt{hc}, \emph{etc}. % \item Poles derived from \meta{coffin1}, such as % \texttt{\meta{coffin1}-l}, \texttt{\meta{coffin1}-r}, % \texttt{\meta{coffin1}-hc}, \emph{etc.} % \item Poles derived from \meta{coffin2}, such as % \texttt{\meta{coffin2}-l}, \texttt{\meta{coffin2}-r}, % \texttt{\meta{coffin2}-hc}, \emph{etc.} % \end{enumerate} % % Applying this ability allows a series of joining operations to % take place, as illustrated in Figure~\ref{fgr:nested}. In this % example, the scheme used for alignment was as follows: % \begin{verbatim} % \SetHorizontalCoffin\OutputCoffin{} % \SetHorizontalCoffin\RedCoffin % {\color{red!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[vc,hc]\RedCoffin[vc,hc] % \SetHorizontalCoffin\BlueCoffin % {\color{blue!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[\RedCoffin-vc,\RedCoffin-hc] % \BlueCoffin[b,l] % \SetHorizontalCoffin\GreenCoffin % {\color{green!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[\BlueCoffin-vc,\BlueCoffin-hc] % \GreenCoffin[b,l] % \SetHorizontalCoffin\YellowCoffin % {\color{yellow!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[\GreenCoffin-vc,\GreenCoffin-hc] % \YellowCoffin[b,l] % \SetHorizontalCoffin \OrangeCoffin % {\color{orange!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[\BlueCoffin-t,\BlueCoffin-l] % \OrangeCoffin[b,r] % \TypesetCoffin\OutputCoffin % \end{verbatim} % This process begins by setting up \cs{OutputCoffin} to hold the joined % output. Each join then takes place placing the new addition relative % to the previous one. As each coffin joined has a unique name it is % possible to align relative to each one of the component parts of the % assembly. This is illustrated by the addition of the final % \cs{OrangeCoffin} based on the earlier placement of the % \cs{BlueCoffin}. % % \begin{figure} % \centering % \SetHorizontalCoffin\OutputCoffin{} % \SetHorizontalCoffin\RedCoffin % {\color{red!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[vc,hc]\RedCoffin[vc,hc] % \SetHorizontalCoffin\BlueCoffin % {\color{blue!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[\RedCoffin-vc,\RedCoffin-hc] % \BlueCoffin[b,l] % \SetHorizontalCoffin\GreenCoffin % {\color{green!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[\BlueCoffin-vc,\BlueCoffin-hc] % \GreenCoffin[b,l] % \SetHorizontalCoffin\YellowCoffin % {\color{yellow!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[\GreenCoffin-vc,\GreenCoffin-hc] % \YellowCoffin[b,l] % \SetHorizontalCoffin \OrangeCoffin % {\color{orange!20!white}\rule{0.2 in}{0.2 in}} % \JoinCoffins\OutputCoffin[\BlueCoffin-t,\BlueCoffin-l] % \OrangeCoffin[b,r] % \TypesetCoffin\OutputCoffin % \caption{Aligning coffins using poles from previous operations.} % \label{fgr:nested} % \end{figure} % % \section{Typesetting coffins} % % \begin{function}{\TypesetCoffin} % \begin{syntax} % \cs{TypesetCoffin} % ~~\meta{coffin} [ \meta{pole1} , \meta{pole2} ] % ~~( \meta{x-offset} , \meta{y-offset} ) % \end{syntax} % Typesetting is carried out by first calculating \meta{handle}, the % point of intersection of \meta{pole1} and \meta{pole2}. This is an % optional argument, and if not given then |(H,l)|, the \TeX{} % reference point of the underlying box, is used. The coffin % is then typeset such that the relationship between the current % reference point in the document and the \meta{handle} is described % by the \meta{x-offset} and \meta{y-offset}. This \meta{offset} is % optional, and if not given |(0 pt, 0 pt)| is used. Typesetting a % coffin is therefore analogous to carrying out an alignment where the % \enquote{parent} coffin is the current insertion point. % \end{function} % % \section{Measuring coffins} % % There are places in the design process where it is useful to be able to % measure coffins outside of pole-setting procedures. % % \begin{function}{\CoffinDepth} % \begin{syntax} % \cs{CoffinDepth} \meta{coffin} % \end{syntax} % Calculates the depth (below the baseline) of the \meta{coffin} % in a form suitable for use in a \meta{dimension expression}, for example % |\setlength{\mylength}{\CoffinDepth\ExampleCoffin}|. % \end{function} % % \begin{function}{\CoffinHeight} % \begin{syntax} % \cs{CoffinHeight} \meta{coffin} % \end{syntax} % Calculates the height (above the baseline) of the \meta{coffin} % in a form suitable for use in a \meta{dimension expression}, for example % |\setlength{\mylength}{\CoffinHeight\ExampleCoffin}|. % \end{function} % % \begin{function}{\CoffinTotalHeight} % \begin{syntax} % \cs{CoffinTotalHeight} \meta{coffin} % \end{syntax} % Calculates the total height of the \meta{coffin} % in a form suitable for use in a \meta{dimension expression}, for example % |\setlength{\mylength}{\CoffinTotalHeight\ExampleCoffin}|. % \end{function} % % \begin{function}{\CoffinWidth} % \begin{syntax} % \cs{CoffinWidth} \meta{coffin} % \end{syntax} % Calculates the width of the \meta{coffin} in a form % suitable for use in a \meta{dimension expression}, for example % |\setlength{\mylength}{\CoffinWidth\ExampleCoffin}|. % \end{function} % % \section{Diagnostic functions} % % Diagnostic data for following the coffin-building process is % available both graphically and at the terminal. This reflects the % fact that coffins are visual constructs. % % \begin{function}{\DisplayCoffinHandles} % \begin{syntax} % \cs{DisplayCoffinHandles} \meta{coffin} \Arg{color} % \end{syntax} % This function first calculates the intersections between all of % the \meta{poles} of the \meta{coffin} to give a set of % \meta{handles}. It then prints the \meta{coffin} at the current % location in the source, with the position of the \meta{handles} % marked on the coffin. The \meta{handles} will be labelled as part % of this process: the locations of the \meta{handles} and the labels % are both printed in the \meta{color} specified. % \end{function} % % \begin{function}{\MarkCoffinHandle} % \begin{syntax} % \cs{MarkCoffinHandle} \meta{coffin} % ~~[ \meta{pole1} , \meta{pole2} ] \Arg{color} % \end{syntax} % This function first calculates the \meta{handle} for the % \meta{coffin} as defined by the intersection of \meta{pole1} and % \meta{pole2}. It then marks the position of the \meta{handle} % on the \meta{coffin}. The \meta{handle} will be labelled as part of % this process: the location of the \meta{handle} and the label are % both printed in the \meta{color} specified. If no \meta{poles} are % give, the default |(H,l)| is used. % \end{function} % % \begin{function}{\ShowCoffinStructure} % \begin{syntax} % \cs{ShowCoffinStructure} \meta{coffin} % \end{syntax} % This function shows the structural information about the % \meta{coffin} in the terminal. The width, height and depth of the % typeset material are given, along with the location of all of the % poles of the coffin. For example, for the rotated coffin in % Figure~\ref{fgr:rotation}, the output of \cs{ShowCoffinStructure} % is: % \begin{verbatim} % Size of coffin \ExampleCoffin: % > ht = 72.26999pt % > dp = 0.0pt % > wd = 72.26999pt % Poles of coffin \ExampleCoffin: % > l => {0pt}{0pt}{0pt}{1000pt} % > B => {0pt}{0pt}{1000pt}{0pt} % > H => {0pt}{0pt}{1000pt}{0pt} % > T => {0pt}{0pt}{1000pt}{0pt} % > hc => {36.135pt}{0pt}{0pt}{1000pt} % > r => {72.26999pt}{0pt}{0pt}{1000pt} % > vc => {0pt}{36.135pt}{1000pt}{0pt} % > t => {0pt}{72.26999pt}{1000pt}{0pt} % > b => {0pt}{0.0pt}{1000pt}{0pt}. % } % \end{verbatim} % Notice that the poles of a coffin are defined by four values: % the $x$ and $y$ co-ordinates of a point that the pole % passes through and the $x$- and $y$-components of a % vector denoting the direction of the pole. It is the ratio between % the later, rather than the absolute values, which determines the % direction of the pole. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{Implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=coffin> % \end{macrocode} % % \begin{macrocode} \ProvidesExplPackage{xcoffins}{2024-03-14}{} {L3 Experimental design level coffins} % \end{macrocode} % % \begin{variable} % { % \l_@@_A_hpole_tl , % \l_@@_A_vpole_tl , % \l_@@_B_hpole_tl , % \l_@@_B_vpole_tl , % \l_@@_bound_box_grow_bool , % \l_@@_hoffset_dim , % \l_@@_voffset_dim % } % Key--value definitions for the alignment system. With the exception % of \texttt{grow-bounding-box}, all of these have to be given with a % value. % \begin{macrocode} \keys_define:nn { coffin } { coffin1-hpole .tl_set:N = \l_@@_A_hpole_tl , coffin1-hpole .value_required:n = true , coffin1-vpole .tl_set:N = \l_@@_A_vpole_tl , coffin1-vpole .value_required:n = true , coffin2-hpole .tl_set:N = \l_@@_B_hpole_tl , coffin2-hpole .value_required:n = true , coffin2-vpole .tl_set:N = \l_@@_B_vpole_tl , coffin2-vpole .value_required:n = true , grow-bounding-box .bool_set:N = \l_@@_bound_box_grow_bool , grow-bounding-box .default:n = true , hoffset .dim_set:N = \l_@@_hoffset_dim , hoffset .value_required:n = true , voffset .dim_set:N = \l_@@_voffset_dim , voffset .value_required:n = true } \keys_set:nn { coffin } { coffin1-hpole = H , coffin1-vpole = l , coffin2-hpole = H , coffin2-vpole = l , grow-bounding-box = true , hoffset = 0 pt , voffset = 0 pt } % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_design_names:N} % \begin{variable}{\Height, \Depth, \Width, \TotalHeight} % \begin{variable} % {\l_@@_height_dim, \l_@@_depth_dim, \l_@@_width_dim, \l_@@_totalheight_dim} % Sets up design-level names for the various coffin dimensions. These are % not defined outside of this scope, and are dimensions so that they work % correctly inside for example \cs{fp_eval:n}. % \begin{macrocode} \cs_new_protected:Npn \@@_design_names:N #1 { \dim_set:Nn \l_@@_height_dim { \coffin_ht:N #1 } \dim_set:Nn \l_@@_depth_dim { \coffin_dp:N #1 } \dim_set:Nn \l_@@_width_dim { \coffin_wd:N #1 } \dim_set:Nn \l_@@_totalheight_dim { \l_@@_height_dim + \l_@@_depth_dim } \cs_set_eq:NN \Height \l_@@_height_dim \cs_set_eq:NN \Depth \l_@@_depth_dim \cs_set_eq:NN \Width \l_@@_width_dim \cs_set_eq:NN \TotalHeight \l_@@_totalheight_dim } \dim_new:N \l_@@_height_dim \dim_new:N \l_@@_depth_dim \dim_new:N \l_@@_width_dim \dim_new:N \l_@@_totalheight_dim % \end{macrocode} % \end{variable} % \end{variable} % \end{macro} % % A lot of this is more-or-less just passing data straight through. % % \begin{macro}{\NewCoffin} % This is a very easy conversion. % \begin{macrocode} \NewDocumentCommand \NewCoffin { m } { \coffin_new:N #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{\SetHorizontalCoffin} % \begin{macro}{\SetVerticalCoffin} % These are again straight-forward translations. % \begin{macrocode} \NewDocumentCommand \SetHorizontalCoffin { m +m } { \hcoffin_set:Nn #1 {#2} } \NewDocumentCommand \SetVerticalCoffin { m m +m } { \vcoffin_set:Nnn #1 {#2} {#3} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\SetHorizontalPole} % \begin{macro}{\SetVerticalPole} % Here, there is a need to set up the design-level names for coffin % dimensions. This requires grouping, but the coffin work has to occur % outside of the group. Hence there is a bit of expansion trickery. % \begin{macrocode} \NewDocumentCommand \SetHorizontalPole { m m m } { \group_begin: \@@_design_names:N #1 \use:e { \group_end: \coffin_set_horizontal_pole:Nnn #1 { \exp_not:n {#2} } { \dim_eval:n {#3} } } } \NewDocumentCommand \SetVerticalPole { m m m } { \group_begin: \@@_design_names:N #1 \use:e { \group_end: \coffin_set_vertical_pole:Nnn #1 { \exp_not:n {#2} } { \dim_eval:n {#3} } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\JoinCoffins} % The \cs{JoinCoffins} function needs to do a bit of work on the input % syntax, as there are a number of optional arguments to worry about. % The idea here is that \cs{JoinCoffins} can be used to either expand % the bounding box of \meta{coffin1} or add \meta{coffin2} without any % expansion of the bounding box. There are also the two handle positions % and the offset to sort out. % \begin{macrocode} \NewDocumentCommand \JoinCoffins { o s m > { \SplitArgument { 1 } { , } } O { H , l } m > { \SplitArgument { 1 } { , } } O { H , l } > { \SplitArgument { 1 } { , } } D ( ) { 0 pt , 0 pt } } { \IfNoValueTF {#1} { \IfBooleanTF #2 { \coffin_attach:NnnNnnnn #3 #4 #5 #6 #7 } { \coffin_join:NnnNnnnn #3 #4 #5 #6 #7 } } { \group_begin: \keys_set:nn { coffin } {#1} \tl_set:Ne \l_@@_tmp_tl { \group_end: \bool_if:NTF \l_@@_bound_box_grow_bool { \coffin_join:NnnNnnnn } { \coffin_attach:NnnNnnnn } \exp_not:N #3 { \exp_not:o { \l_@@_A_hpole_tl } } { \exp_not:o { \l_@@_A_vpole_tl } } \exp_not:N #5 { \exp_not:o { \l_@@_B_hpole_tl } } { \exp_not:o { \l_@@_B_vpole_tl } } { \dim_use:N \l_@@_hoffset_dim } { \dim_use:N \l_@@_voffset_dim } } \l_@@_tmp_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\TypesetCoffin} % For typesetting coffins there are two optional arguments, both of % which need to be split. This is a simpler case of the code needed for % \cs{JoinCoffins}. % \begin{macrocode} \NewDocumentCommand \TypesetCoffin { m > { \SplitArgument { 1 } { , } } O { H , l } > { \SplitArgument { 1 } { , } } D ( ) { 0 pt , 0 pt } } { \coffin_typeset:Nnnnn #1 #2 #3 } % \end{macrocode} % \end{macro} % % \begin{macro}{\RotateCoffin} % \begin{macro}{\ResizeCoffin} % \begin{macro}{\ScaleCoffin} % Mores straight-forward copies. % \begin{macrocode} \NewDocumentCommand \RotateCoffin { m m } { \coffin_rotate:Nn #1 {#2} } \NewDocumentCommand \ResizeCoffin { m m m } { \coffin_resize:Nnn #1 {#2} {#3} } \NewDocumentCommand \ScaleCoffin { m m m } { \coffin_scale:Nnn #1 {#2} {#3} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\CoffinDepth, \CoffinHeight, \CoffinTotalHeigth, \CoffinWidth} % Nothing too complex, except that the total height is set up as an % expression so that it will act correctly if prefixed with a negative % sign, \emph{etc.} % \begin{macrocode} \NewDocumentCommand \CoffinDepth { m } { \dim_eval:n { \coffin_dp:N #1 } } \NewDocumentCommand \CoffinHeight { m } { \dim_eval:n { \coffin_ht:N #1 } } \NewDocumentCommand \CoffinTotalHeight { m } { \dim_eval:n { \coffin_ht:N #1 + \coffin_dp:N #1 } } \NewDocumentCommand \CoffinWidth { m } { \dim_eval:n { \coffin_wd:N #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\DisplayCoffinHandles} % Displaying all of the handles is a bit easier, as there is no need % to worry about the handle. % \begin{macrocode} \NewDocumentCommand \DisplayCoffinHandles { m m } { \coffin_if_exist:NT #1 { \coffin_display_handles:Nn #1 {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\MarkCoffinHandle} % Marking a handle requires a bit of work with the input, so that % the design-level interface is \enquote{nice}. % \begin{macrocode} \NewDocumentCommand \MarkCoffinHandle { m > { \SplitArgument { 1 } { , } } O { H , l } m } { \coffin_if_exist:NT #1 { \coffin_mark_handle:Nnnn #1 #2 {#3} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\ShowCoffinStructure} % Back again to easy-to-implement functions. % \begin{macrocode} \NewDocumentCommand \ShowCoffinStructure { m } { \coffin_show_structure:N #1 } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex