5

I would like to find a way to horizontally align any two or more random places of my document (but probably on the same page or not too far from each other). It's a bit like the second column of a tabular, but:

  1. the "rows" can be separated by any amount of whatever. (The tabular can be splitted between the rows.)
  2. the points to be aligned can't always be seen as belonging to a row or cell of a tabular, because they can appear in the middle of a normal paragraph without the rest of the paragraph being affected by this alignment.

How to obtain that all bullets be aligned in the following examples (replacing minuses with white space)?

(1) This sentence is missing X. ---------∙ What is this sentence missing?

Some comment/paragraph(s) in between.

(2) X is missing a subject. -------------∙ What/Who is missing a subject?

Some comment/paragraph(s) in between.

(3) My friends invited X to the party. ---∙ Who did my friends invite to the party?

Some comment/paragraph(s) in between.

(4) -------------------------------∙ Who did my friends invite?

And alike in the following paragraphs (replacing minuses with blank space)?

This paragraph horizontally jumps-------∙ at some point for some reason. The rest of the text can span other the next lines.

This paragraph wants its jumps to land ---∙ exactly at the same absolute horizontal position as the jump from the above paragraph (not quite perfectly executed here).

How to behave when the position is reached before jumping ∙ is undefined but could be defined by jumping to the next line and aligning there.

The jump can also happen in the second line of the paragraph, so that it looks more like -------------------------------∙ this jump.

What (I think) I need is a mecanism to remember some horizontal position (for example 30pt from the left margin) (something like a label remembering neither the last produced number, nor the page it is in, but the absolute horizontal position it has-maybe a tikz node?), then recover it later in the document and use it to horizontally position something at this absolute position (30pt from the left margin).

One specific situation where I need this is with linguistic examples that are built parallel but appear at different parts of the document (see above). Espacially if they are on the same page, it would be good visual semantics to show their belonging together through this alignment. There, the point 2 above doesn't hold, so I used remembering a tabular preamble with tabu in the MWE at the end.

One problem with this solution is that any further left part of an example that is longer than the left part of the first example gets broken. I need to manually add some \hspace at the end of the first cell. But it's something I'm ok with, because I seldom have to align more than a few examples.

  This sentence misses X. \hspace*{4em} & What does this sentence miss?\\

And the second problem is that using a tabular is not always an option as shown in my second case above. See the end of the MWE for that second problem, solved with manually ajusting all \hspace commands:

\documentclass{article}
\usepackage{expex}
\usepackage{tabu}

\begin{document}

\ex \begin{tabu}[t]{X[-1]@{\hspace{1em}}X}\savetabu{mypreamble}
  This sentence misses X. & What does this sentence miss?\\
\end{tabu}
\xe

Comments in between.

\ex \begin{tabu}{\usetabu{mypreamble}}
  X misses a subject. & Who/what misses a subject?\\
\end{tabu}
\xe

Another comment.

\ex \begin{tabu}{\usetabu{mypreamble}}
  My friends invited X to the party. & Who did my Friends invite to the party?\\
\end{tabu}
\xe

This paragraph horizontally jumps \hspace*{3em} \textbullet\ at some 
point for some reason. The rest of the text can span other the next lines.

This paragraph wants its jumps to land \hspace*{.5em} \textbullet\ 
exactly at the same absolute horizontal position as the jump from the 
above paragraph (not quite perfectly executed here).

How to behave when the position is reached before jumping \textbullet\ is undefined.

The jump can also happen in the second line of the paragraph, so that it 
looks like \hspace*{16em} \textbullet\ this jump. 

\end{document}

How can I achieve the case in the examples in an even more automated fashion?

How can I achieve this at all within paragraphs?

bullets to be aligned through examples and paragraphs

4

The following example puts the question in a \hbox to the right. The width of the box is measured beforehand using the longest question:

\documentclass{article}
\usepackage[
  hmargin=1.5in,
]{geometry}
\usepackage{enumerate}

\newdimen\QuestionLength
\newcommand*{\QuestionFormat}[1]{\textbullet~#1}%
\settowidth{\QuestionLength}{%
  \QuestionFormat{Who did my friends invite to the party?}%
}
\newcommand*{\question}[1]{%
  \leavevmode
  \unskip
  \hspace*{1em plus 1fill}\hbox to \QuestionLength{%
    \QuestionFormat{#1}\hfill
  }%
  \par
}

\begin{document}
\begin{enumerate}[(1)]
\item
  This sentence is missing X.
  \question{What is this sentence missing?}

  Some comment/paragraph(s) in between.

\item
  X is missing a subject.
  \question{What/Who is missing a subject?}

  Some comment/paragraph(s) in between.

\item
  My friends invited X to the party.
  \question{Who did my friends invite to the party?}

  Some comment/paragraph(s) in between.

\item
  \question{Who did my friends invite?}
\end{enumerate}
\end{document}

Result

Version with fixes space to the left

The width of the largest text on the left is put in \LeftPartLength. Then the width of the question is calculated by using the current line width minus the length of the left part and some separation space. An error is thrown, if there is not enough place for the question.

\documentclass{article}
\usepackage[
  hmargin=1.5in,
]{geometry}
\usepackage{enumerate}

\newcommand*{\QuestionFormat}[1]{\textbullet~#1}%
\newdimen\QuestionLength
\newdimen\LeftPartLength
\newdimen\MiddleSep
\setlength{\MiddleSep}{1em}
\settowidth{\QuestionLength}{%
  \QuestionFormat{Who did my friends invite to the party?}%
}
\settowidth{\LeftPartLength}{%
   Some comment/paragraph(s) in between.%
}

\newcommand*{\question}[1]{%
  \leavevmode
  \unskip
  \begingroup
    \dimen0=\dimexpr\linewidth - \LeftPartLength - \MiddleSep\relax
    \ifdim\dimen0<\QuestionLength
      \errmessage{Question is large by %
        \the\dimexpr\QuestionLength-\dimen0\relax.}%
    \fi
    \hspace*{\MiddleSep plus 1fill}%
    \hbox to \dimen0{%
      \QuestionFormat{#1}\hfill
    }%
  \endgroup
  \par
}

\begin{document}
\begin{enumerate}[(1)]
\item
  This sentence is missing X.
  \question{What is this sentence missing?}

  Some comment/paragraph(s) in between.

\item
  X is missing a subject.
  \question{What/Who is missing a subject?}

  Some comment/paragraph(s) in between.

\item
  My friends invited X to the party.
  \question{Who did my friends invite to the party?}

  Some comment/paragraph(s) in between.

\item
  \question{Who did my friends invite?}
\end{enumerate}
\end{document}

Result

  • Works almost fine. But it somehow pushes the right-hand parts of the examples (the questions) very far to the right if they are all quite short. To avoid these big spaces in the middle, the solution would be to calculate the width needed for the \hbox with something like \linewidth - length of longest left-part. Any ideas? If not, I will accept your answer for the first case. – benjamin Mar 25 '16 at 20:11
  • @benjamin See updated answer. – Heiko Oberdiek Mar 25 '16 at 20:26
  • I belive that a solution use zref-savepos package. – touhami Mar 25 '16 at 20:37
3

Thanks to touhami's suggestion, I found more general solutions using zref-savepos. Refer to the package's documentation for some explanations. One difficulty seems to be that absolute positions are hard to deal with, whereas relative positions are ok. It takes up to 5 compilations to get things right!

Here the commented code:

\documentclass[a4paper,10pt,DIV13]{scrartcl}
\usepackage{expex}
\usepackage{tabu}
\usepackage{ifthen}
\usepackage{calc}
\usepackage{zref-user}
\usepackage{zref-savepos}

\begin{document}

% because the bullets where here only to explicit what I wanted to align
% I didn't include them in the macros

% first the more general solution for paragraphs

\newlength{\tab}% the length of the jumps
\newcommand{\banchor}[1]{\zsaveposx{#1}}% choose the reference point

\newcommand{\balign}[2]{% the 1st argument is the anchorname of the current jump
                        % the 2nd argument is the name of the general anchor chosen
                        % through \banchor
\rule{0pt}{0pt}% to avoid trouble at the beginning of lines
\zsaveposx{#1}\zrefused{#1}% set the reference point from where you want to jump
\setlength{\tab}{\zposx{#2}sp-\zposx{#1}sp}% calculate the length of the jump
\ifthenelse{\lengthtest{\tab<0pt}}% if it's a negative jump,
  {\\\rule{0pt}{0pt}\zsaveposx{#1improbablename}%
    % then begin a new line and set the reference at its beginning
    % with a new anchor name
  \setlength{\tab}{\zposx{#2}sp-\zposx{#1improbablename}sp}% recalculate the jump
  } %
  {}%
\parbox[l]{\tab}{\rule{0pt}{0pt}}% use a \parbox to jump a exact unstrechtable length
% \hspace*{\tab} doesn't work here, because it's strechtable
}

This paragraph horizontally jumps\hspace*{4em}\banchor{B}\textbullet\ at  some point
for some reason. The rest of the text can span other the next lines. 
% this jump is arbitrary, the next ones (previous ones would also work)
% will adapt and align with it

This paragraph wants its jump to land\balign{Ba}{B}\textbullet\ exactly at the same
absolute horizontal position as the jump from the above paragraph (quite perfectly
executed here).

\balign{Bb}{B}\textbullet\ jumping directly to the point is also an option.

How to behave when the position is reached before jumping\balign{Bc}{B}\textbullet\ could
be defined by jumping to the next line.

The jump can also happen The jump can also happen The jump can also happen in the second
line of the paragraph, so that it looks like
\balign{Be}{B}\textbullet\ this jump. 

\bigskip
\hrule

% now to the linguistic examples, following the same principles, 
% but a bit adapted for the more specific needs of the linguistic  examples:
% compile and detect where to put the aligning anchor with \bmark
% use \btab to align the other right parts with it

\newlength{\tabmin}\setlength{\tabmin}{1em}

\newcommand{\bmark}[1]{%
\zsaveposx{#1}\zrefused{#1}\hspace*{\tabmin}}

\newcommand{\btab}[2]{%
\zsaveposx{#1}\zrefused{#1}%
\setlength{\tab}{\tabmin+\zposx{#2}sp-\zposx{#1}sp}%
\hspace*{\tab}}% \hspace* is ok, because wrapped lines are unexpected

\ex This sentence misses X.\btab{Aa}{A}\textbullet\ What does this sentence  miss?
\xe

Comments/explanations/paragraphs in between.

\ex X misses a subject.\btab{Ab}{A}\textbullet\ Who/what misses a subject?
\xe

Another comment.

\ex My friends invited X to the party.\bmark{A}\textbullet\ Who
did my friends invite to the party?
\xe

Another comment.

\ex \btab{Ac}{A}\textbullet\ Who did my friends invite?
\xe

\end{document}

aligned text from different paragraphs

Edit

Now I've replaced \parbox[l]{\tab}{\rule{0pt}{0pt}} and \hspace*{\tab} with a simple rule, which can help visualise the exact jump if set with non-zero height \rule{\tab}{0pt}.

Improvement still pending: my approach needs the user to find as many different names as jumps needed in the document. Maybe a counter could automatise this part of the job, so that the commands \balign and \btab only take one argument.

Testing these macros in other places (in tabulars, or when dealing with graphics, etc.) might show some serious limitations.

As for now, this solution is my favorite one.

  • Be aware, however, that the insertion of the calculated horizontal space affects the paragraphing computations, so it might well happan that TeX finds different line breaks in the second run and moves the start of the jump to a different x-position. The length of the jump must therefore be recomputed, and there is no guarantee that the process converges. Indeed, I’ve been able to build an example in which the \balign commands keeps on alternatively understimating and overstimating the tabbing amount, and never gets to the right one. – GuM Mar 26 '16 at 20:09
  • True! I tried and I too found a case where the process never converges. So a limitation is that it's not guaranteed if more than one jump appear in the same paragraph. And again: to those who want to use it, be patient, it sometimes converges after a lot of compilations (yes, maybe 10 or so). – benjamin Mar 30 '16 at 12:16
  • Well, actually my example that never converges involves a single paragraph that contains a single jump (a single \balign). – GuM Mar 31 '16 at 17:50
3

First approximation

The \parbox solution works only for very short texts. Here is a more elaborate program:

\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{lipsum}

\makeatletter

\def\do#1{\@ifdefinable #1{\newdimen #1}}
% the above is because \newlength allocates a \skip
\do\JumpIndentation
\do\JPprevpardepth

% To allow customization:
\@ifdefinable\EveryJumpingPar{
    \newtoks\EveryJumpingPar \EveryJumpingPar={}
}

\newcommand*\StartJumpingPar{\par
    \JPprevpardepth \prevdepth
    \setbox\z@ \vbox\bgroup
        \color@begingroup % not sure it's really necessary
        \parfillskip \@flushglue
        \prevdepth \JPprevpardepth
        \indent \the\EveryJumpingPar
}
\newcommand*\Jump{%
        \par
        \color@endgroup
    \egroup
    \setbox\tw@ \vbox{%
        \unvcopy\z@
        \setbox\tw@ \lastbox % note that "\indent\par" yields at least one box
        \global \setbox\@ne \hbox{\unhbox\tw@}%
        \global \dimen@i \lastskip % should be -- finger crossed! -- the 
                                  % interline glue above the last box
        \unskip \unpenalty % remove interline glue and widow penalty
        \setbox\tw@ \lastbox % if non-void, assume it is penultimate line
        \global \dimen3 \ifvoid\tw@
            \JPprevpardepth
        \else
            \dp\tw@
        \fi
    }%
    \unvbox\z@
    \ifdim \wd\@ne<\JumpIndentation
        % backspace vertically by one line
        \skip@ \parskip
        \advance \skip@ \ht\@ne
        \advance \skip@ \dp\@ne
        \advance \skip@ \dimen@i
        \vskip -\skip@
        \prevdepth \dimen3
    \else
        \prevdepth \dp\@ne
    \fi
    {\parindent \JumpIndentation \indent}%
    \ignorespaces
}

\makeatother

\setlength{\JumpIndentation}{.57\textwidth} % set as desired



\begin{document}

Let's begin with an ordinart paragraph.

\lipsum[1]

\StartJumpingPar
Now a ``jumping paragraph''.
\lipsum*[1]\Jump\lipsum*[2]

Another ordinary (\emph{i.e.}, ``non-jumping'') paragraph in between, consisting
of at least two lines of text.

\StartJumpingPar
Note that all ``jump'' positions\Jump are vertically aligned, as required.

\StartJumpingPar
If the horizontal position to jump to has already been passed,\Jump the
\verb|\Jump| commands jumps to the next line, as required too.

\StartJumpingPar
Let's try, this time, to get aaaaalmost to\Jump the point!  Now we try again, 
with just one more~``a''.

\StartJumpingPar
Let's try, this time, to get aaaaaalmost to\Jump the point!  Are you satisfied?

\EveryJumpingPar{\textbullet\ }

\StartJumpingPar
Moreover, you can use the token register \verb|\EveryJumpingPar| to customize 
the appearance of\Jump your ``jumping paragraphs''.

That's all, folks!

\end{document}

The output produced by this code is:

Output of 1st approximation

This should be regarded as a first approximation to the answer: I’ll wait for the OP’s comments before introducing possible enhancements.


Second approximation

This time we define an environment called JumpingParagraphs. This environment must contain only ordinary paragraphs, and nothing else. Within each paragraph encompassed by this environment, the \Jump command can be used to tabulate to the horizontal position specified by the \JumpIndentation rigid length. The \Jump command may be used an unlimited number of times within the same paragraph.

\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{lipsum}

\makeatletter

\def\do#1{\@ifdefinable #1{\newdimen #1}}
% the above is because \newlength allocates a \skip
\do\JumpIndentation
\do\@JP@normal@indent
\do\@JP@prevpardepth

% To allow customization:
\@ifdefinable\EveryJumpingPar{
    \newtoks\EveryJumpingPar \EveryJumpingPar={}
}

\@ifdefinable\@JP@saved@par{} % just check that the name can be used
\newenvironment*{JumpingParagraphs}{%
    \@JP@normal@indent \parindent
    \let\@JP@saved@par\par
    \def\par{\@JP@par@close\@JP@start@paragraph}%
    \@JP@start@paragraph
}{%
    \@JP@par@close
}
\newcommand*\@JP@start@paragraph{%
    \@JP@par@open\@JP@normal@indent{\the\EveryJumpingPar}%
}
\newcommand*\@JP@par@open[2]{%
    \@JP@prevpardepth \prevdepth
    \setbox\z@ \vbox\bgroup
        \color@begingroup % not sure it's really necessary
        \parfillskip \@flushglue
        \prevdepth \@JP@prevpardepth
        {\parindent #1\indent #2}%
        \ignorespaces
}
\newcommand*\@JP@par@close{%
        \@JP@saved@par
        \global \dimen@i \prevdepth
        \color@endgroup
    \egroup
    \unvcopy\z@
    \prevdepth \dimen@i % for the moment
}
\newcommand*\Jump{%
    \@JP@par@close
    \setbox\z@ \vbox{%
        \unvbox\z@
        \setbox\z@ \lastbox % note that "\indent\par" yields at least one box
        \global \setbox\@ne \hbox{\unhbox\z@}%
        \global \dimen@i \lastskip % should be -- finger crossed! -- the 
                                   % interline glue above the last box
        \unskip \unpenalty % remove interline glue and widow penalty
        \setbox\z@ \lastbox % if non-void, assume it is penultimate line
        \global \dimen3 \ifvoid\z@
            \@JP@prevpardepth
        \else
            \dp\z@
        \fi
    }%
    \ifdim \wd\@ne<\JumpIndentation
        % backspace vertically by one line
        \skip@ \parskip
        \advance \skip@ \ht\@ne
        \advance \skip@ \dp\@ne
        \advance \skip@ \dimen@i
        \vskip -\skip@
        \prevdepth \dimen3
        \dimen@ \ht\@ne
    \else
        \prevdepth \dp\@ne
        \dimen@ \z@
    \fi
    \@JP@par@open\JumpIndentation{\vrule \@width\z@ \@height\dimen@}%
}

\makeatother

\setlength{\JumpIndentation}{.57\textwidth} % set as desired



\begin{document}

Let's begin with an ordinary paragraph.

\lipsum[1]

\begin{JumpingParagraphs}
% No empty lines here!
Now a ``jumping paragraph''.
\lipsum*[1]\Jump\lipsum*[2]
\Jump Regrettably, \verb|Overfull \hbox|'es might occur: this is almost
inevitable, given the fact that a line containing a ``jump'' places a double
constraint on the choice of the breakpoints.

Another ordinary (\emph{i.e.}, ``non-jumping'') paragraph in between, consisting
of at least two lines of text.  Note that it occurs \emph{within} a 
\texttt{JumpingParagraphs} environment.

Note that all ``jump'' positions\Jump are vertically aligned, as required.

If the horizontal position to jump to has already been passed,\Jump the
\verb|\Jump| commands jumps to the next line, as required too.

Let's try, this time, to get aaaaalmost to\Jump the point!  Now we try again, 
with just one more~``a''.

Let's try, this time, to get aaaaaalmost to\Jump the point!  Are you satisfied?

Moreover, you can use the token register \verb|\EveryJumpingPar| to customize 
the appearance of\Jump your ``jumping paragraphs''.  However, it must be set 
\emph{outside} of the \texttt{JumpingParagraphs} environment.

The \verb|\Jump| command can also be used\Jump several times\Jump within a
single paragraph.  And in-line mathematics (say, \( y=\sin(x) \)) may\Jump
occur, too.  As it is quite obvious, you can both jump \emph{to} math\Jump\(
y=\frac{1}{x} \), and \emph{from} math (the last equation is better
written as \( y=1/x \),\Jump isn't it?).  It is also possible to use unusually
high formulas, as in \( \displaystyle
\biggl(\frac{1}{x}+\frac{1}{y}\biggr)\biggl(\frac{1}{x}-\frac{1}{y}\biggr) \),
and, even in case of a\Jump jump, the spacing will be correct!

But alas, \( \displaystyle \frac{x}{y}+\frac{y}{x}=\frac{x^{2}+y^{2}}{xy} \)
\Jump fails miserably: the spacing is correct for the second half of the line,
but not for the first.  You should keep in mind this limitation.  On the other
hand,\Jump \( \displaystyle \frac{x}{y}+\frac{y}{x}=\frac{x^{2}+y^{2}}{xy} \)
works in a more or less acceptable way, as you can see.

\textbf{Main limitation:} a \texttt{JumpingParagraphs} environment cannot
contain \emph{anything} but ordinary paragraphs.
% No empty lines here!
\end{JumpingParagraphs}

This paragraph is outside the \texttt{JumpingParagraphs} environment.  Note 
that line spacing is correct

\bigbreak

\EveryJumpingPar = {\textbullet\ }

We have just set \verb|\EveryJumpingPar| to contain \verb*|\textbullet\ |.  Now 
we start another \texttt{JumpingParagraphs} environment.

\begin{JumpingParagraphs}
    $X$~misses something.
    \Jump What is $X$ missing?

    This sentence misses~$X$.
    \Jump What is missing?

    This sentence $X$ something.
    \Jump What is missing?
\end{JumpingParagraphs}

That's all, folks!

\end{document}

Third (and fourth) approximation

The second approximation did not handle correctly the case of multiple \par tokens appearing in the input, as it could result from extra blank lines. The third approximation corrects this flaw, by having recourse to \everypar to trigger the \@JP@start@paragraph macro, and also adds the \EveryJump token register:

\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{lipsum}
\usepackage{color}

\definecolor{subdued}{gray}{.75}

\makeatletter

\def\do#1{\@ifdefinable #1{\newdimen #1}}
% the above is because \newlength allocates a \skip
\do\JumpIndentation
\do\@JP@normal@indent
\do\@JP@prevpardepth

% To allow customization:
\def\do#1{\@ifdefinable #1{\newtoks #1#1{}}}
\do\EveryJumpingPar
\do\EveryJump

\@ifdefinable\@JP@saved@par{} % just check that the name can be used
\@ifdefinable\@JP@next{}
\newenvironment*{JumpingParagraphs}{%
    \everypar{\@JP@start@paragraph}%
}{%
    \par
}
\newcommand*\@JP@start@paragraph{%
    \@JP@normal@indent \parindent
    \let\@JP@saved@par\par
    \setbox\z@ \lastbox % get indentation box
    \@@par % return to vertical mode with an empty paragraph
    \ifvoid\z@
        \let \@JP@next \noindent
    \else
        \let \@JP@next \indent
    \fi
    \@JP@par@open\@JP@next\@JP@normal@indent{\the\EveryJumpingPar}%
}
\newcommand*\@JP@par@open[3]{%
    % #1 <- either "\indent" or "\noindent"
    % #2 <- indentation amount
    % #3 <- tokens to insert at the beginning of the paragraph
    \@JP@prevpardepth \prevdepth
    \setbox\z@ \vbox\bgroup
        \color@begingroup % not sure it's really necessary
        \parfillskip \@flushglue
        \everypar{}%
        \let\par\@JP@par@close
        \prevdepth \@JP@prevpardepth
        {\parindent #2#1#3}%
        \ignorespaces
}
\newcommand*\@JP@par@close{%
        \@JP@saved@par
        \global \dimen@i \prevdepth
        \color@endgroup
    \egroup
    \unvcopy\z@
    \prevdepth \dimen@i % for the moment
}
\newcommand*\Jump{%
    \@JP@par@close
    \setbox\z@ \vbox{%
        \unvbox\z@
        \setbox\z@ \lastbox % note that "\indent\par" yields at least one box
        \global \setbox\@ne \hbox{\unhbox\z@}%
        \global \skip\@ne \lastskip % should be -- finger crossed! -- the 
                                    % interline glue above the last box
        \unskip \unpenalty % remove interline glue and widow penalty
        \setbox\z@ \lastbox % if non-void, assume it is penultimate line
        \global \dimen@i \ifvoid\z@
            \@JP@prevpardepth
        \else
            \dp\z@
        \fi
    }%
    \ifdim \wd\@ne<\JumpIndentation
        % backspace vertically by one line
        % Note: no "\parskip" glue is added in internal vertical mode when the
        %       current list is empty, so we don't need to compensate for it!
        \skip@ \skip\@ne
        \advance \skip@ \ht\@ne
        \advance \skip@ \dp\@ne
        \vskip -\skip@
        \prevdepth \dimen@i
        \dimen@   \ht\@ne
        \dimen@ii \dp\@ne
    \else
        \prevdepth \dp\@ne
        \dimen@   \z@
        \dimen@ii \z@
    \fi
    \@JP@par@open\indent\JumpIndentation{%
        % It was trivial to fix this one!
        \vrule \@width\z@ \@height\dimen@ \@depth\dimen@ii \relax
        % Now "\relax" is no longer necessary, actually.
        \the\EveryJump
    }%
}

\makeatother

\setlength{\JumpIndentation}{.57\textwidth} % set as desired



\begin{document}

Let's begin with an ordinary paragraph.

\lipsum[1]

\begin{JumpingParagraphs}

% Empty lines are now allowed here.

Now a ``jumping paragraph''.
\lipsum*[1]\Jump\lipsum*[2]
\Jump Regrettably,\hfil\verb|Overfull \hbox|'es might occur: this is almost
inevitable, given the fact that a line containing a ``jump'' places a double
constraint on the choice of the breakpoints.

% Multiple blank lines in the input are now handled correctly:


Another ordinary (\emph{i.e.}, ``non-jumping'') paragraph in between, consisting
of at least two lines of text.  Note that it occurs \emph{within} a 
\texttt{JumpingParagraphs} environment.

Note that all ``jump'' positions\Jump are vertically aligned, as required.

If the horizontal position to jump to has already been passed,\Jump the
\verb|\Jump| commands jumps to the next line, as required too.

Let's try, this time, to get aaaaalmost to\Jump the point!  Now we try again, 
with just one more~``a''.

Let's try, this time, to get aaaaaalmost to\Jump the point!  Are you satisfied?

\EveryJumpingPar = {\textbullet\ }

Moreover, you can use the token register \verb|\EveryJumpingPar| to customize 
the appearance of\Jump your ``jumping paragraphs''.

\EveryJump = {\textasteriskcentered\ }

In the same way, the \verb|\EveryJump| tokens\Jump are inserted each time a
\verb|\Jump| command\Jump is executed, as it is exemplified by this same\Jump
paragraph.

These token registers can now also be set inside the \texttt{JumpingParagraphs}
environment itself.

\EveryJumpingPar = {}
\EveryJump = {}

The \verb|\Jump| command can also be used\Jump several times\Jump within a
single paragraph.  And in-line mathematics (say, \( y=\sin(x) \)) may\Jump
occur, too.  As it is quite obvious, you can both jump \emph{to} math\Jump\(
y=\frac{1}{x} \), and \emph{from} math (the last equation is better
written as \( y=1/x \),\Jump isn't it?).  It is also possible to use unusually
high formulas, as in \( \displaystyle
\biggl(\frac{1}{x}+\frac{1}{y}\biggr)\biggl(\frac{1}{x}-\frac{1}{y}\biggr) \),
and, even in case of a\Jump jump, the spacing will be correct!

And now, also \( \displaystyle \frac{x}{y}+\frac{y}{x}=\frac{x^{2}+y^{2}}{xy} \)
\rlap{\color{subdued}sminnief}\Jump functions with absolute perfection: line
spacing is correct both for the first half of the line and for the second one.
On the other hand,\Jump \( \displaystyle
\frac{x}{y}+\frac{y}{x}=\frac{x^{2}+y^{2}}{xy} \) works in a less satisfactory
way, as you can see.  You should keep in mind this limitation.

\textbf{Main limitation:} a \texttt{JumpingParagraphs} environment cannot
contain \emph{anything} but ordinary paragraphs.

\noindent Note that \verb|\noindent| commands\Jump \emph{are} honored: indeed,
this has now been corrected (in an update).

\begingroup

\setlength{\parindent}{4em}

Also, \verb|\parindent| may now vary\Jump from one paragraph\Jump to another:
I~had completely overlooked this in the previous versions!

Another paragraph with the same\Jump different indentation.

\endgroup

Back to previous indentation,\Jump with a jump.

One last paragraph without jumps.

% Empty lines are now allowed here.

\end{JumpingParagraphs}

This paragraph is outside the \texttt{JumpingParagraphs} environment.  Note 
that line spacing is correct.

\bigbreak

\newcounter{sentence}
\renewcommand*{\thesentence}{{\normalfont (\textit{\alph{sentence}})}}
\EveryJumpingPar = {\refstepcounter{sentence}\llap{\thesentence\ }}
\EveryJump = {\textbullet\ }

We have just set \verb|\EveryJumpingPar| to contain a numbered label, which is
automatically stepped, and \verb|\EveryJump| to contain \verb*|\textbullet\ |.
Now we start another \texttt{JumpingParagraphs} environment.

\begin{JumpingParagraphs}
    \setlength{\parskip}{\medskipamount}

    $X$~misses something.
    \Jump What is $X$ missing?

    This sentence misses~$X$.
    \Jump What is missing?

    This sentence $X$ something.
    \Jump What is missing?

    Now, get close to the jump destination...
    \Jump to be sure that the baselines are lined up properly.
\end{JumpingParagraphs}

That's all, folks!

\end{document}

This time I’ll show the output too:

Output of 3rd/4th approximation

The solution that employs zref-savepos is indeed more flexible than this; however, I think that it might be of some interest to show how the goal can be achived entirely with good ol’ “plain-TeX-style” methods: no dependence on the \pdfsavepos and related primitives operating at \shipout-time, no need for a two-pass compilation, just playing around with boxes and glue.


I must plainly admit it, this question has caught me! I am repeatedly refining my answer, and I’ll probably end up with posting a complete package…

  • This does the job. Unfortunately, I don't understand the code so I can't really hope to make enhancements myself. One thing could be added, if possible with this approach: the exact \JumpIndentation could be automatically extracted from some anchored point in the document, like I did in my answer using zref-savepos. But because I found an answer that does it, don't waste time developing your approach further. – benjamin Mar 26 '16 at 0:40
  • @benjamin: Please refrain from accepting this answer yet: it’s still unfinished (I spoke of “first approximation”). To begin with, multiple \Jump commands within the same paragraph are not supported. This can be corrected, but I was awaiting input from you on how you are going to use the commands. For example, would it be acceptable for you to enclose every “jumping paragraph” in an environment? Or would you prefer an environment that encompasses several such paragraphs? – GuM Mar 26 '16 at 0:56
  • as you will have seen in my own solution with zref-savepos, the mechanism I hoped to find was to deal with 2 commands: one to define a horizontal anchor point at any user-chosen position, and one to use this anchor when adding some tab command (\Jump) refering to this anchor anywhere else in the document, at best without having to define the paragraphs as anything special like JumpingParagraphs. – benjamin Mar 26 '16 at 8:53
3

This one has long been, and still was, in my “to do” list…

I said that this question caught my imagination, and indeed I am here again with another proposal for a solution, based on a different approach; and exactly because the approach is different, I deem this is one of those rare cases in which it is more correct to post a new answer that to edit the one I have already given (all the more so, in that it would be the fifth major addition!). I am also going to keep my promise to supply, this time, a complete and independent package that provides the required functionality.


Analysis of the other solutions

Before presenting the new solution, however, I’d like to discuss briefly why I think that the ones that have already been given (including mine) are not completely satisfactory.

Drawbacks of my solution

The solution presented in the other answer I gave is based on a simple principle: in order to jump to a tab position, we do the following:

  1. we interrupt the current paragraph, breaking into lines the part of it that comes before the tabbing command;

  2. we measure the width of the last line, to see if it reached as far from the left margin as to have already gone past the tab position;

  3. if it has not, we backspace by one line of text, in order to print the continuation line at the same vertical position as the line that came before the tab;

  4. we typeset the continuation line starting with an indentation equal to the tab position.

The critical points in this algorithm are step 2 and 3: in step 2, in order to measure the width of the last line, that is, of the last box in the paragraph, you need to retrieve that box from the current vertical list; in step 3, in order to find out the exact amount of vertical backspace required to align perfectly the continuation line with the line that came before the tab, you need to inspect, and hence retrieve as well from the current vertical list, the so-called “interline glue” that TeX has inserted before the latter. But both of this operations are forbidden (leaving TeXnical details alone) in the normal mode of operation TeX is in when it is stacking paragraphs on the page: you need to enter “internal vertical mode”, and this accounts for the restrictions that had to be imposed on the contents of a JumpingParagraphs environment. These restrictions could be somewhat lifted by taking a slightly different approach (which, however, would increase the burden put on TeX’s memory), but even in this case two things would remain impossible:

  • to include floats;

  • to have footnotes and marginal notes.

These limitations could be acceptable, if they were effective; unfortunately, even with this method it cannot be guaranteed that the right alignment would be achieved in all cases, as the last example presented in my other answer shows.

Drawbacks of @benjamin’s answer

The answer based on the use of zref-savepos measures, at \shipout-time, the horizontal position at which the “start” of the tabulation occurs, and uses this information to insert, during a subsequent LaTeX run, a horizontal space that is supposed to shift the ensuing text at the position one wants to tabulate to. However, the insertion of this space might alter TeX’s line-breaking decision, in such a way that, in the second run, the position of the “start” of the tabulation might change, thus invalidating the previous computation of the amount of horizontal space that should be inserted. Another LaTeX run is therefore required to compute a new value for this horizontal space, but the new value might again change TeX’s line-breaking decisions, and so on. Thus we see that the process is inherently iterative, and this has essentially one downside: there is no guarantee that this iterative process will come to an end, eventually finding a value for the horizontal space that remains the same on subsequent runs. Indeed, it is possible to build examples in which this process does not “converge”; even worse, since the exact horizontal position measured by zref-savepos depends on the computations for glue setting, that can be rounded differently on different machines, it is even possible that, for the same given input file, the process “converges” on some machines and not on others.


The approach used in this answer

This answer uses a hack based on a low-level feature of TeX which permits (provided that some conditions are met) to measure the length of the last line of a partial paragraph even in “outer” vertical mode, that is, in the mode TeX operates in while it is stacking paragraphs in the usual way: see The TeXbook, p. 188, second paragraph, description of \predisplaysize for details. Before measuring that line, the current partial paragraph is divided into lines (well, it wouldn’t make any sense to speak of its “last line”, otherwise!), and the line breaks that have been chosen are “frozen” so that they can no longer change. This approach has the following pros and cons:

  1. It can be used in the normal mode LaTeX operates in, allowing all usual constructs (lists, floating objects, footnotes…) to intervene without disturbing the alignment mechanism.

  2. It works in a single LaTeX run: therefore, it simply cannot suffer from any “convergence” problem.

  3. On the other hand, since the low-level TeX feature on which it is based is not available under some circumstances, also this solution will not work under those same circumstances. Anyway, the code is capable of detecting when this happens, and will consequently issue an appropriate error message.

The package code

As already said above, this time I have written a complete package that implements the proposed solution, called ghoritab. Here is the code:

%%
%% This is file `ghoritab.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% ghoritab.doc 
%% 
%% IMPORTANT NOTICE:
%% 
%% For the copyright see the source file.
%% 
%% Any modified versions of this file must be renamed
%% with new filenames distinct from ghoritab.sty.
%% 
%% For distribution of the original source see the terms
%% for copying and modification in the file ghoritab.doc.
%% 
%% This generated file may be distributed as long as the
%% original source files, as listed above, are part of the
%% same distribution. (The sources need not necessarily be
%% in the same archive or directory.)
%% \CharacterTable
%%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%%   Digits        \0\1\2\3\4\5\6\7\8\9
%%   Exclamation   \!     Double quote  \"     Hash (number) \#
%%   Dollar        \$     Percent       \%     Ampersand     \&
%%   Acute accent  \'     Left paren    \(     Right paren   \)
%%   Asterisk      \*     Plus          \+     Comma         \,
%%   Minus         \-     Point         \.     Solidus       \/
%%   Colon         \:     Semicolon     \;     Less than     \<
%%   Equals        \=     Greater than  \>     Question mark \?
%%   Commercial at \@     Left bracket  \[     Backslash     \\
%%   Right bracket \]     Circumflex    \^     Underscore    \_
%%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%%   Right brace   \}     Tilde         \~}
%%
%% \CheckSum{311}
\NeedsTeXFormat{LaTeX2e}\relax % LaTeX2e is required!
\ProvidesPackage{ghoritab}
                [2016/04/21 v0.03 (development)]
\@ifdefinable\@gHT@ghoritab{\def\@gHT@ghoritab{ghoritab}}
\RequirePackage{ifetex}\relax
\ifetex \else
  \PackageError\@gHT@ghoritab{%
    Not running under e-TeX%
  }{% Write a better help message.
    The \@gHT@ghoritab\space package requires
    e-TeX extensions.\MessageBreak
    This is a fatal error: the recommended action
    is to type\MessageBreak
    X <return> \space to quit. \space
    If, instead, you choose to continue\MessageBreak
    the \@gHT@ghoritab\space package will _not_ be loaded.%
  }
  \expandafter\endinput
\fi
\providecommand*\newrigidlength[1]{\@ifdefinable #1{\newdimen #1}}
\newrigidlength\@gHT@common@height
\newrigidlength\@gHT@common@depth
\newsavebox{\@gHT@preceding@text}
\newsavebox{\@gHT@following@text}
\@ifdefinable\@gHT@strut{\def\@gHT@strut{%
  \vrule \@width\z@
      \@height\@gHT@common@height \@depth\@gHT@common@depth
}}
\@ifdefinable\@gHT@measure@box{\def\@gHT@measure@box#1#2{%
  \setbox#1\hbox{#2}%
  \@gHT@common@height
      \ifdim\ht#1>\ht\strutbox \ht#1\else \ht\strutbox \fi
  \@gHT@common@depth
      \ifdim\dp#1>\dp\strutbox \dp#1\else \dp\strutbox \fi
}}
\@ifdefinable\@gHT@action{}
\newcommand*\@gHT@parse@left[1][]{%
  \leavevmode
  \@gHT@measure@box\@gHT@preceding@text{%
    \color@begingroup #1\unskip \color@endgroup
  }%
  \@gHT@parse@right
}
\newcommand*\@gHT@parse@right[2][]{%
  \@gHT@measure@box\@gHT@following@text{%
    \color@begingroup \@gHT@strut\nobreak #1\color@endgroup
  }%
  \@gHT@measure@box\@gHT@preceding@text{%
    \unhbox\@gHT@preceding@text \@gHT@strut
  }%
  \unhbox\@gHT@preceding@text
  \begingroup
    $$%
      \abovedisplayskip      \z@skip
      \abovedisplayshortskip \z@skip
      \belowdisplayskip      \z@skip
      \belowdisplayshortskip \z@skip
      \predisplaypenalty  \@M
      \postdisplaypenalty \@M
      \global \dimen@i \predisplaysize
      \offinterlineskip
    $$%
    \count@ \prevgraf
    \advance \count@ -\thr@@
    \dimen@
        \ifdim \dimen@i=-\maxdimen \z@ \else \dimen@i \fi
    {\@@par}%
    \parskip \z@skip
    \ifdim \dimen@=\maxdimen
      \@gHT@doNOT@vbackspace
      \noindent
      \@gHT@PDS@error{}%
%%    \else \ifnum \gluestretchorder\leftskip>\z@
%%      \@gHT@RLS@err % should include call to \@gHT@left@line
%%    \else \ifnum \glueshrinkorder\leftskip>\z@
%%      \@gHT@RLS@err
    \else
      \advance \dimen@ -\tw@ em
      \@gHT@action{#2}%
    \fi % \fi\fi
    \prevgraf \count@
  \endgroup
  \unhbox\@gHT@following@text
  \let \@gHT@action \@undefined
}
\@ifdefinable\@gHT@ll@horitab{\def\@gHT@ll@horitab#1{%
  \dimen@ii #1\relax
  \ifdim \dimen@<\dimen@ii
    \@gHT@DO@vbackspace
  \else
    \@gHT@doNOT@vbackspace
  \fi
  \@gHT@indent@to@tab
}}
\@ifdefinable\@gHT@ll@settab{\def\@gHT@ll@settab#1{%
  \global #1=\dimen@
  \dimen@ii \dimen@
  \@gHT@DO@vbackspace
  \@gHT@indent@to@tab
}}
\@ifdefinable\@gHT@DO@vbackspace{\def\@gHT@DO@vbackspace{%
  \skip@ \@gHT@common@height
  \advance \skip@ \@gHT@common@depth
  \vskip -\skip@
  \nointerlineskip
  \advance \count@ \m@ne
}}
\@ifdefinable\@gHT@doNOT@vbackspace{\def\@gHT@doNOT@vbackspace{%
  \prevdepth \@gHT@common@depth
}}
\@ifdefinable\@gHT@indent@to@tab{\def\@gHT@indent@to@tab{%
  \advance \dimen@ii -\leftskip
  \advance \dimen@ii -\parshapeindent\numexpr\count@+\@ne\relax
  \parindent \dimen@ii
  \indent
}}
\@ifdefinable\@gHT@PDS@error{\def\@gHT@PDS@error#1{%
  \PackageError{\@gHT@ghoritab}{%
    Cannot measure horizontal position%
  }{%
    To measure the horizontal position of a tab,
    this package relies\MessageBreak
    on a primitive feature of TeX that is not available
    in some situations.\MessageBreak
    This entails that it is impossible either to set,
    or to jump to,\MessageBreak
    a tab position in any of the following contexts:\MessageBreak
    \MessageBreak
    \space\space - when either \protect\centering\space or
    \protect\raggedleft\space (or similar declarations)\MessageBreak
    \space\space\space\space is in force;\MessageBreak
    \MessageBreak
    \space\space - when the current position on the line has already
    abutted the\MessageBreak
    \space\space\space\space current right margin;\MessageBreak
    \MessageBreak
    \space\space - when you have applied some special settings to the
    last line\MessageBreak
    \space\space\space\space of paragraphs (e.g., non-infinitely
    stretchable \protect\parfillskip\MessageBreak
    \space\space\space\space glue or non-zero
    \protect\lastlinefit;\MessageBreak
    \MessageBreak
    \space\space - in every other situation in which
    \protect\predisplaysize\space is made\MessageBreak
    \space\space\space\space  equal to \protect\maxdiman.\MessageBreak
    \MessageBreak
    Type \space <return> to proceed, but expect unexpected results.%
  }%
}}
\newcommand*\gotoghtab{%
  \let \@gHT@action \@gHT@ll@horitab
  \@gHT@parse@left
}
\newcommand*\setghtab{%
  \let \@gHT@action \@gHT@ll@settab
  \@gHT@parse@left
}
\endinput
%%
%% End of file `ghoritab.sty'.

Unfortunately, the 30000 character limit did not permit me to include any comments, but a version of the above code that is extensively commented and is formatted according to the usual DocStrip conventions also exists. The code shown above (which was extracted from the documented source) should be saved in a file called ghoritab.sty, inside a directory where TeX can find it when compiling your source code; as usual, this means either:

a) in the same directory as the .tex source you want to compile;

b) in your personal texmf tree, say in

    $TEXMFHOME/tex/latex/ghoritab.sty

or more deeply nested position;

c) in your local (machine-wide) texmf tree, e.g.,

    $TEXMFLOCAL/tex/latex/ghoritab.sty

or a more deeply nested position
(of course, in this case appropriate access privileges are needed).

Using the package

The main command provided by the package is named \gotohtab. In its simplest form, its usage is as follows:

\gotohtab{<dimen>}

where <dimen> is a (rigid) length specified either explicitly, e.g., 5cm, or through a “length command”, e.g., \myLength. The effect of this command is to jump to a horizontal position that is <dimen> away from the prevailing left margin, irrespective of changes in the current left margin due to lists, unusual \parshapes, etc.; if the text in the current line has already surpassed this horizontal position, the command jumps to the same position on the next line.

There is a corresponding command named \setghtab that globally sets the value of a length command passed in its (mandatory) argument to the distance from the prevailing (see comments above) left margin of the position on the line at which the command is given. Usage (without optional arguments, see below):

\setghtab{<length-command>}

The <length-command> passed as the argument of \setghtab need not be (although it can be) a “rubber length”: a “rigid” length (TeX’s <dimen>) suffices. For this reason, the package defines the declaration \newrigidlength, which is analogous to \newlength, but allocates a rigid length command (that is, a <dimen> register) instead of a rubber one (that is, instead of a <skip> register). Thus, you can say

\newrigidlength{\mytabposition}

to allocate a length command named \mytabposition to be subsequently used with \setghtab/\gotoghtab. Note that

\newlength{\mytabposition}

will work too, but the former declaration should appeal to people concerned about sparing <skip> registers… :-)

One more point remains to be covered: the optional arguments of the \gotoghtab and \setghtab commands, and how they are used to overcome an inherent limitation of the method we have employed: it’s not able to cope automatically with the case of “unusual” line spacing.

When used without optional arguments, the \gotoghtab and \setghtab commands assume that the line they occur on contains text with “normal” height and depth. If this is not the case, for example because of a tall in-line formula, you may end up with the two halves of that line that are misaligned. The optional arguments provide a workaround for this: it suffices to place the portion of text that is causing trouble, because of its anomalous vertical dimensions, inside those arguments, more precisely inside the first one if it is text that precedes the tab position, and inside the second one if it is text that follows it. For example

\gotoghtab[unusual text before][unusual text after]{\mytabposition}

If only one optional argument is present, it is assumed to refer to the “text before”. Note that including more text than actually necessary does no harm: in practice, you should start with no optional arguments and, when you see a misalignment in the output, move some amount of text inside one of them until the problem vanishes, without worrying of moving too much of it.

An example

The following sample code illustrates the use of the ghoritab package:

\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{ghoritab}
\usepackage{color}

\definecolor{subdued}{gray}{.75}

\newrigidlength{\tabposA}
\newrigidlength{\tabposB}
\newrigidlength{\tabposC}

\newcommand*{\meta}[1]{\(\langle\textit{#1\/}\rangle\)}
\newcommand*{\vissettab}[1]{\rule[-.5ex]{.2pt}{2ex}\setghtab{#1}}

\hfuzz = 0pt
\vfuzz = 0pt



\begin{document}

\section{Basic usage}
\label{S:Basic}

First try:\gotoghtab{5cm}and then continue the paragraph with more text, that
reaches at least as far as to the second line.  Some more text, and now another
jump\gotoghtab{5cm}to the same position.

Next, let's show how to set a tab position in the middle of an ordinary
paragraph: you simply have to write
\verb|\setghtab{|\meta{length-command}\verb|}| at the exact spot where you want
the tab to be set.  For instance, we put one immediately after this little
vertical rule:~\vissettab{\tabposA} The paragraph may continue afterwards as
usual; note, however, that all the line breaks that precede the tab stop have
been ``frozen'' (lest the tab position change place!).  In this case, the tab
position has been set at \the\tabposA\ from the left margin.  Note that the
\meta{length-command} is always set equal to the tab position \textbf{globally}.

You may then tab to\gotoghtab{\tabposA}that same horizontal position and, if the
position you want to tab to has already been passed, a jump to the same
position, but on the next line, will occur.  A single paragraph can contain as
many \verb|\gotoghtab| commands as you want; for instance, this one contains
two: the one above, and another one\gotoghtab{\tabposA}here.

Get closer\gotoghtab{\tabposA}and closer and closer and closer and closer and
closer and closer\gotoghtab{\tabposA}and closer and closer and closer and closer
and closer and closer and\gotoghtab{\tabposA}closer and closer and closer and
closer and closer and closer and closer and\gotoghtab{\tabposA}closer and closer
and closer and closer and closer and closer and closer and
closer\gotoghtab{\tabposA}and this time we jump to the next line because the tab
position has already been passed.  Now we'll repeat this same paragraph, but
using the optional arguments of the \verb|\gotoghtab| command.

Get \gotoghtab[closer][and closer and closer and closer and closer]{\tabposA}
and closer \gotoghtab[and closer][and closer]{\tabposA} and closer and closer
and closer and closer \gotoghtab[and closer and][closer]{\tabposA} and closer
and closer and closer and closer and closer and closer \gotoghtab[and][closer
and closer and closer]{\tabposA} and closer and closer and closer and closer and
\gotoghtab[closer][and this time]{\tabposA} we jump to the next line because the
tab position has already been passed.  Now we'll repeat this same paragraph, but
using the optional arguments of the \verb|\gotoghtab| command.

No, this time we actually don't.  Rather, let's set another tab stop a little
closer to the right margin, let's say just here.~\vissettab{\tabposB}

Now we want to show that:
%
\begin{enumerate}
    \item
        the tab is honored\gotoghtab{\tabposB}even inside lists;

    \item
        this is true at all levels\gotoghtab{\tabposB}of nesting,
        \begin{itemize}
            \item  as you can see\gotoghtab{\tabposB}here,
            \item  and again\gotoghtab{\tabposB}here,
            \item  and also in a case in which the tab 
                position\gotoghtab{\tabposB}has already been passed, as here;
        \end{itemize}

    \item
        this remains true, of course, also with longer list items that take up 
        more than one line, as it happens with the present item, in which the 
        jump to the tab position doesn't occur until\gotoghtab{\tabposB}here.
\end{enumerate}
%
Back to the outer\gotoghtab{\tabposB}level.



\section{Usage with an explicit \texttt{\char`\\ parshape}}
\label{S:parshape}

Here we use the \emph{second} tab position that we set in the previous section
(the one closer to the right margin) with a custom \verb|\parshape| that we
explictly set.  We recall that the tab positions falls\gotoghtab{\tabposB}here.

\begingroup

\setlength\parindent{0pt}
\parshape 15
    10mm 130mm   15mm 124mm   20mm 118mm   25mm 112mm   30mm 106mm
    35mm 100mm   40mm 94mm    45mm 88mm    50mm 82mm    55mm 77mm
    60mm 71mm    65mm 65mm    70mm 59mm    75mm 53mm    80mm 47mm
%
In this paragraph the left margin moves towards the center at a pace of
$5\,\mbox{mm}$ per line, and the right margin does the same at a pace of
$1\,\mbox{mm}$ per line.  Does this interfere with our tabulation mechanism?
Well, it depends: for what concern the tab position
itself,\gotoghtab{\tabposB}it doesn't, as you can see here, and, continuing on
the next line,\gotoghtab{\tabposB}also here.  On the other hand, sooner or later
\verb|Overfull \hbox| warnings are guaranteed to appear, for the simple
reason\gotoghtab{\tabposB}that the line becomes too narrow for the tab
position\gotoghtab{\tabposB}that has been set.  But in this example we give
up\gotoghtab{\tabposB}before this happens, because we don't like such warnings!
In exchange, we \emph{set} a new tab stop right here:~\vissettab{\tabposC} we'll
use this new tab position after resuming the normal margins.

\endgroup

As you expect, the tab position\gotoghtab{\tabposC}set up inside the paragraph 
having custom \verb|\parshape| is honored in ``normal'' paragraphs too, as you 
can see again\gotoghtab{\tabposC}here, and also\gotoghtab{\tabposC}here.



\section{Setting the tab inside a list}
\label{S:Lists}

Generalizing what we saw in section~\ref{S:Basic}, we want to show that\ldots
%
\begin{itemize}
    \item
        \ldots you can also \emph{set} a tab stop inside a list\ldots

    \item
        \ldots also, let's say\ldots
        \begin{itemize}
            \item  \ldots at the second level, as we do here~\vissettab{\tabposC}
            \item  (another item).
        \end{itemize}

    \item
        then you can:
        \begin{enumerate}
            \item  use that stop\gotoghtab{\tabposC}at the same level
            \item  (but with a different kind of list,\gotoghtab{\tabposC}as 
                here); \emph{or}
            \item  use that tab at an inner level,
                \begin{itemize}
                    \item  as it is done\gotoghtab{\tabposC}here
                    \item  (and here is another\gotoghtab{\tabposC}example); 
                        \emph{or}
                \end{itemize}
        \end{enumerate}

    \item
        use always the same tab stop at\gotoghtab{\tabposC}an outer level; 
        \emph{or}, finally\ldots
\end{itemize}
%
\ldots use it at the level of normal text, that is,\gotoghtab{\tabposC}at the
outermost level.

Well, until now, everything seems fine, and our tab mechanism seems to perform
pretty well: you say ``Jump!'',\gotoghtab{\tabposC}and it jumps where you want;
you order ``Again!'',\gotoghtab{\tabposC}and it does it again, perhaps on the
next line; you can also set several tab positions within the same paragraph,
say, one here~\vissettab{\tabposA} and another one here~\vissettab{\tabposB}
(plus the one set inside the list, that, don't forget,
lies\gotoghtab{\tabposC}here); the tame ``printing head'' will
jump\gotoghtab{\tabposA}here,\gotoghtab{\tabposB}and
here,\gotoghtab{\tabposC}and here.  But the next section will reveal a drawback
of the algorithm\ldots



\section{Making up for incorrect line spacing}
\label{S:Interline}

When used without optional arguments, the \verb|\gotoghtab| and \verb|\setghtab|
commands assume that the line they occur on contains text with ``normal'' height
and depth.  If this is not the case, for example because of a tall in-line
formula like \( \displaystyle \frac{x}{y}-\frac{y}{x} = \frac{(x+y)(x-y)}{xy}
\), say,\gotoghtab{\tabposB}as you can see here the two halves of that line will
not be aligned.  Let us make another test using rules (a few more words, in
order to get to the next
line):~\rule[-5mm]{.4pt}{5mm}\gotoghtab{\tabposB}\rule{.4pt}{5mm}~The optional
arguments have been introduced exactly to provide a workaround for this.

When used without optional arguments, the \verb|\gotoghtab| and \verb|\setghtab|
commands assume that the line they occur on contains text with ``normal'' height
and depth.  If this is not the case, for example because of a tall in-line
formula \gotoghtab[like \( \displaystyle \frac{x}{y}-\frac{y}{x} =
\frac{(x+y)(x-y)}{xy} \), say,\rlap{\textcolor{subdued}{..\kern 1.6pt
aas}}]{\tabposB}as you can see here the two halves of that line will not be
aligned.  Let us make another test using rules (a few more words, in order to
get \gotoghtab[{to the next
line):~\rule[-5mm]{.4pt}{5mm}}\rlap{\textcolor{subdued}{.....\rule{.4pt}{5mm}}}]
[\rule{.4pt}{5mm}~The optional]{\tabposB} arguments have been introduced exactly
to provide a workaround for this.

\end{document}

The following pictures show the three pages you should obtain by typesetting it:

Page 1 Page 2 Page 3

1

For the case of paragraphs, I found a solution using a \parbox to jump over its text. I can't tell if \hbox would be a better Idea. This solves the cases where the jump happens in the first line of a paragraph.

\newlength{\jumplength}
\setlength{\jumplength}{20em} % adjust manually
\newcommand{\jumping}[1]{\parbox[l]{\jumplength}{#1}}

\jumping{This paragraph horizontally jumps}\textbullet\ at some point
for some reason. The rest of the text can span other the next lines.

\jumping{This paragraph wants its jump to land}\textbullet\ exactly at  
the same absolute horizontal position as the jump from the above paragraph.

\jumping{~}\textbullet\ jumping directly to the point is also an option.

alignment of points in different paragraphs

And for the sake of exactitude, Heiko's second answer should read

\settowidth{\LeftPartLength}{%
My friends invited X to the party.%
}

to exactly correspond to my needs, which gives a slightly different result:

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.