Is there a way to skip to a fixed position on a line? (a specific tab position)

Example:

The quick \skiptoposition{1.5cm} brown \skiptoposition{2.5cm}fox


which would put "brown" 1.5cm and "fox" 2.5cm from left margin?

-
There is a tabbing environment, but I don't think that is what you need. This could be done using either tikz or the zref package by measuring the current position. –  Martin Scharrer Nov 2 '11 at 20:08
Thank you Martin. I have had great replies to my question! –  Dan Nov 4 '11 at 10:20

The already mentioned tabbing package has the disadvantage that it cannot be used across paragraphs, for instance, in itemize environments, as asked in this related question.

However, the tabto package provides exactly what you are looking for via the command \tabto*:

\documentclass{article}
\usepackage{tabto}

\begin{document}
The quick \tabto{3.5cm}brown \tabto{5.5cm}fox
\par
\tabto{3.5cm}jumps too\tabto{5.5cm}far
\par\medskip
The quick \tabto*{1.5cm}brown \tabto*{3.5cm}fox
\par
\tabto{1.5cm}jumps too\tabto{3.5cm}far
\end{document}


-
But that seems to result in a new line at each tabto? –  Peter Grill Nov 3 '11 at 16:33
@PeterGrill: Yes, but only if the target distances are to small, that is, left from the current position in the current line (which was the case for the distances in the example of the OP). I have modified the code accordingly. –  Daniel Nov 3 '11 at 19:40
Thanks Daniel, this is almost perfect for my usage. I don't suppose there is any way of overriding the behavior of starting a new line if the tab is to the left of the current position. For my purposes I would like it to go to the defined tab position even if it caused text to overlap. –  Dan Nov 3 '11 at 21:12
@Dan: Apparently this is possible by first doing a negative \hskip that is wide enough to then \tabto to the intended position. I have updated my solution accordingly (using a ridiculously large value for the negative \hskip). –  Daniel Nov 3 '11 at 21:39
@Dan: My zref-solution allows for absolute spacing relative to text left margin within the given line. Or is that not what you're after? –  Werner Nov 3 '11 at 21:44

This is what tabbing is for:

\documentclass{article}
\pagestyle{empty}
\begin{document}
\begin{tabbing}
\hspace*{2cm}\=\hspace*{2cm}\=\kill
The quick \> brown \>fox\\
\end{tabbing}
\end{document}


-
'tabbing' environment works nicely but interferes with making boxes etc. Would like to do: The \vskip0pt\vskip-(back to baseline)\hskip1.5cm brown fox but do not know how to get the value to do the reverse skip back to the previous baseline. If I crunch a value in it works but I need it to work in all fonts/sizes etc. –  Dan Nov 2 '11 at 20:38
This needs to be \hspace*{1.5cm}\=\hspace*{1.0cm}\=\kill to produce the correct output –  Peter Grill Nov 2 '11 at 20:45
@peter-grill: I increased the spacing to make the effect more visible. –  Boris Nov 2 '11 at 20:51
Thanks Boris, the tabbing environment doesn't quite work for me in this scenario, but thank you for taking the time to answer my question. –  Dan Nov 4 '11 at 10:19

Less complicated than Werner's, but using more or less the same idea:

\noindent\makebox[2.5cm][l]{\makebox[1.5cm][l]{The quick}brown}fox


Just as an exercise, here is a different version implementing the idea of backspacing of Dan's comment:

\documentclass[a4paper]{article}
\usepackage{environ}

\newcommand{\startatpos}[2]{\hspace{#1}#2\hfill\cr\ignorespaces}
\NewEnviron{fixedpos}
{\par\noindent\ooalign{\BODY\crcr}\ignorespacesafterend}

\pagestyle{empty}

\begin{document}
\noindent\rule{1.5cm}{1pt}\par
\noindent\rule{2.5cm}{1pt}\par
\noindent\rule{3.5cm}{1pt}\par

\begin{fixedpos}
\startatpos{0cm}{The}
\startatpos{1.5cm}{quick}
\startatpos{2.5cm}{brown}
\startatpos{3.5cm}{fox}
\end{fixedpos}

\end{document}


-
Is there no way of ending a paragraph with a zero vertical advance (staying on the same baseline), then simply doing a \hskip to the desired horizontal position? eg. The \backskip\hskip1.5cm quick \backskip\hskip2.5cm brown \backskip\hskip3.5cm fox I don't know how to do the \backskip (ie. go to beginning of line) –  Dan Nov 2 '11 at 23:35
Thanks egreg. Great example for me to study further. Thanks for taking the time to help me. –  Dan Nov 4 '11 at 10:18

One way would be to use \hspace{} to control the skip. The one complication is that you need to figure out how far you are to determine the second skip amount and that is where the calc package comes in. Here is a macro version where you specify the positioning along with the text:

\AbsolutePosition[leading text]{position 1}{text 2}{position 2}{text 3}


For example:

\AbsolutePosition{1.5cm}{brown}{2.5cm}{fox}

\AbsolutePosition[The quick]{1.5cm}{brown}{2.5cm}{fox}


yields the first two lines between the guide rules. The last two are to show that it works with font size changes as well (since you mentioned this in the comments):

\documentclass{article}
\usepackage{calc}

\newlength{\skipamount}
\newcommand*{\AbsolutePosition}[5][]{%
\noindent#1%                                 typeset first text
\setlength{\skipamount}{#2-\widthof{#1}}%    determine position of second text
\setlength{\skipamount}{#4-#2-\widthof{#3}}% determine position of third text
}%

\begin{document}
\noindent\rule{1.5cm}{1pt} 1.5cm\par
\AbsolutePosition{1.5cm}{brown}{2.5cm}{fox}\par
\AbsolutePosition[The quick]{1.5cm}{brown}{2.5cm}{fox}\par

\tiny
\AbsolutePosition{1.5cm}{brown}{2.5cm}{fox}\par
\AbsolutePosition[The quick]{1.5cm}{brown}{2.5cm}{fox}\par

\noindent\rule{2.5cm}{1pt} 2.5cm\par
\end{document}


Another method is to use a tabular environment. One complication in this case is the inter column spacing. To make this example easier to read I used newcolumntype from the array package. First column is left empty to produce the spacing, and the second column is set to a width to get the next column in the correct spot. The width of the last column is not that important (needs to be wide enough to accommodate the text):

\documentclass{article}
\usepackage{array}

\newcolumntype{P}[1]{@{}p{#1}@{}}

\begin{document}
\noindent
\begin{tabular}{P{1.5cm}P{1.0cm}P{2.0cm}}
&brown&fox
\end{tabular}
\end{document}


One advantage of this method is that position is absolute, so if the first word does not fit, the second word is still in the correct spot. Replace brown with brownbrown in the above example to see this effect.

-
'tabbing' environment works nicely but interferes with making boxes etc. Would like to do: The \vskip0pt\vskip-(back to baseline)\hskip1.5cm brown fox but do not know how to get the value to do the reverse skip back to the previous baseline. If I crunch a value in it works but I need it to work in all fonts/sizes etc. –  Dan Nov 2 '11 at 20:39
use \widthof to compute the size of the text and this will adjust for different fonts as I did above with skipamount. If you want to align with something else (as opposed to absolute position), you could use \phantom but perhaps that is a separate question. –  Peter Grill Nov 2 '11 at 20:45
Updated \AbsolutePosition macro to accept an optional first parameter with the leading text that is to be displayed similar to the example that you gave. –  Peter Grill Nov 3 '11 at 1:36
Thanks Peter. Your 'tabular' solution appears to work well for my needs, but requires further study for me to understand it (never seen the array package before). Your 'AbsolutePosition' command is great, and I can see all sorts of uses for that. Thanks very much for taking the time to help. –  Dan Nov 4 '11 at 10:12

Short TeX solution (you need to pdfTeX rather than pdfLaTeX).

\newdimen\unit
\def\point(#1,#2)#3;{\rlap{\kern#1\unit
\raise#2\unit\hbox{$\scriptstyle\bullet\;(#1,#2)$ #3 }}}

\hbox{\unit=10pt
\point(0,0) first;
\point(8,0) second;
\point(20,0) third test;
}
\bye


Don't know why Knuth calls such routines dirty though, in Appendix D, of the TeX Book! You can add a fourth point \point(20,2) fourth test; and see it can also tab vertically!

-
Maybe something like \def\point(#1,#2)#3 would be clearer than using spaces to delimit the arguments. E.g., \point(0,0){first} –  TH. Nov 2 '11 at 22:26
@TH You right, I edited the post. –  Yiannis Lazarides Nov 2 '11 at 22:29

Not sure about the elegance of this solution, but you could use an overlap via \rlap{<stuff>}, which sets its contents <stuff> in a zero-width box that is left-aligned - similar to \makebox[0pt][l]{<stuff>}, hence overlapping on the right. Horizontal alignment/spacing is provided by \hspace:

\documentclass{article}
\begin{document}
\noindent\rule{1.5cm}{1pt}\hspace*{2cm}\verb!1.5cm! from left margin \par% Just for reference
\noindent\rule{2.5cm}{1pt}\hspace*{1cm}\verb!2.5cm! from left margin \par% Just for reference
\noindent\rlap{The quick}\rlap{\hspace*{1.5cm}brown}\hspace*{2.5cm}fox
\end{document}​


The horizontal rules above is just for reference to show the distance from the left margin as 1.5cm and 2.5cm respectively.

Edit: Here is an updated version that provides \skiplmargin[<len>]{<stuff>} which places <stuff> exactly <len> from the left margin. <len> is optional with a default of 0pt which is flush with the left margin.

\documentclass{article}
\usepackage[savepos]{zref}% http://ctan.org/pkg/zref
\newcounter{posmarker}% Position marker counter
\newcommand{\skiplmargin}[2][0pt]{%
\stepcounter{posmarker}% To avoid multiple references
\zsavepos{currentloc\theposmarker}% Save current location
\hskip\dimexpr-\zposx{currentloc\theposmarker}sp+\zposx{leftmargin}sp+#1\relax% Move left
#2% Print argument
\hskip\dimexpr\zposx{currentloc\theposmarker}sp-\zposx{leftmargin}sp-#1\relax% Move right
}
\AtBeginDocument{\zsavepos{leftmargin}}
\begin{document}
\noindent\rule{1.5cm}{1pt} \hspace*{2.5cm}\verb!1.5cm! from left margin \par
\noindent\rule{2.5cm}{1pt} \hspace*{1.5cm}\verb!2.5cm! from left margin \par
\noindent The quick~\skiplmargin[1.5cm]{brown}~\skiplmargin[2.5cm]{fox}. \par
\noindent The quick brown fox.

\bigskip
\noindent\rule{3cm}{1pt} \hspace*{5cm}\verb!3cm! from left margin \par
\noindent\rule{7cm}{1pt} \hspace*{1cm}\verb!7cm! from left margin \par
\noindent The quick~\skiplmargin[3cm]{brown fox}~jumps~\skiplmargin[7cm]{over the lazy dog}. \par
\noindent The quick brown fox jumps over the lazy dog.
\end{document}


The main driver is zref's savepos module. A PDF position marker is placed at the start of \skiplmargin to memorize the location in the current line. Subsequently, a skip relative to the left margin is performed, <stuff> is typeset, and the skip is reversed. Space utilized by typesetting <stuff> is preserved, making it seem like the <stuff> was shifted out-of-place.

Caveat: You need to use ties ~ around \skiplmargin in order to preserve the spacing, which is otherwise lost due to the movement. Also, in twoside document mode with possibly different left and right margins, this approach may require some adjustment. This is due to the execution of \zsavepos{leftmargin} at the beginning of the document (\AtBeginDocument) in order to identify the left margin.

-
Is there no way of ending a paragraph with a zero vertical advance (staying on the same baseline), then simply doing a \hskip to the desired horizontal position? eg. The \backskip\hskip1.5cm quick \backskip\hskip2.5cm brown \backskip\hskip3.5cm fox I don't know how to do the \backskip (ie. go to beginning of line) –  Dan Nov 2 '11 at 23:43
It is possible to perform negative skips, but the way TeX constructs lines into paragraphs and paragraphs into pages make it hard to know "where you are exactly" on a line. I'll type up a solution that does this and report back. –  Werner Nov 2 '11 at 23:55
@Dan: See my updated answer using the zref package. –  Werner Nov 3 '11 at 7:40
This is great Werner. The zref package is really interesting, and I can see lots of uses for it. I have had great replies to this question. Thanks so much for your help, and excellent example code. –  Dan Nov 4 '11 at 9:53