14

I am trying to insert additional vertical space at the top, but instead this vertical space is inserted after the first title line:

enter image description here

However, if I comment out \TikzDecorations[2], I obtain the correct spacing.

The desired output is has the vertical space before the first title line:

enter image description here

Update:

  • A simpler test case is now provided which does not use \parshape.

Notes:

  • The code below seems a bit long, but the relevant macro is \TypesetTitle and the fact that \TikzDecorations is invoked.

  • In the MWE one solution is to move the \TikzDecorations to be after the text, but in my actual use case I would prefer to have it first so I don't have to deal with the opacity/foreground/background issues.

References:

Code: Simpler Test Case

\documentclass{article}
\usepackage{showframe}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{layout}

\usepackage[textwidth=5.0cm]{geometry}

\newcommand*{\Text}{%
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae, felis.%
}%


\newcommand*{\MyAboveSkip}{5pt}
\newcommand*{\MyBotSkip}{5pt}
\newcommand*{\TypesetTitle}[1]{%
    %% See comments in https://tex.stackexchange.com/q/7676/4301
    \hbox{}\kern-\topskip  
    \vspace*{\MyAboveSkip}
    {\bfseries\centering#1\par}%
    \vspace*{\MyBotSkip}%
}%

\NewDocumentCommand{\TikzDecorations}{%
    O{2}% #1 = Number of lines for title
}{%
    \noindent
    \begin{tikzpicture}[remember picture,overlay]
    \coordinate (X) at ([
            xshift=1.0in+\hoffset+\oddsidemargin,
            yshift=-1.0in-\voffset-\topmargin-\headheight-\headsep%
        ]current page.north west);

        \draw [red] ([yshift=-2\baselineskip-\MyAboveSkip-\MyBotSkip]X) -- ++ (\linewidth,0);
    \end{tikzpicture}%
}

\begin{document}%\layout
\TikzDecorations[2]
\sloppy\parskip0pt\relax
\TypesetTitle{A Long Title That Requires Two Lines}%
\noindent\Text
\end{document}

Code: Full Test Case

\documentclass{article}
\usepackage{showframe}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{layout}

\newcommand*{\Text}{%
\sloppy\noindent
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae, felis. 
Curabitur dictum
gravida mauris. Nam arcu libero, nonummy eget, consectetuer id, vulputate a, magna. 
Donec vehicula augue eu neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris ut leo. Cras viverra metus rhoncus sem. 
Nulla et lectus vestibulum urna fringilla ultrices.%
}%


\newcommand*{\TitleParShape}{%
    0.40\hsize 0.20\hsize
    0.35\hsize 0.30\hsize
}%
\newcommand*{\BodyParShape}{%
    0.30\hsize 0.40\hsize
    0.25\hsize 0.50\hsize
    0.20\hsize 0.60\hsize
    0.15\hsize 0.70\hsize
    0.10\hsize 0.80\hsize
    0.05\hsize 0.90\hsize
    0.00\hsize 1.00\hsize
}%

\newcommand*{\NumberOfLines}{7 }
\newcommand*{\MyParShape}{%
    \parshape \NumberOfLines
    \BodyParShape
}%

\newcommand*{\MyAboveSkip}{5pt}
\newcommand*{\MyBotSkip}{5pt}
\newcommand*{\TypesetTitle}[1]{%
    %% See comments in https://tex.stackexchange.com/q/7676/4301
    \hbox{}\kern-\topskip  
    \vspace*{\MyAboveSkip}
    \parshape 2
    \TitleParShape
    {\bfseries\centering#1\par}%
    \vspace*{\MyBotSkip}%
}%

\NewDocumentCommand{\TikzDecorations}{%
    O{2}% #1 = Number of lines for title
}{%
    \noindent
    \begin{tikzpicture}[remember picture,overlay]
    \coordinate (X) at ([
            xshift=1.0in+\hoffset+\oddsidemargin,
            yshift=-1.0in-\voffset-\topmargin-\headheight-\headsep%
        ]current page.north west);

        \draw [red] ([yshift=-2\baselineskip-\MyAboveSkip-\MyBotSkip]X) -- ++ (\linewidth,0);
    \end{tikzpicture}%
}

\begin{document}%\layout
\TikzDecorations[2]
\sloppy\parskip0pt\relax
\TypesetTitle{Two Line Title}%
\MyParShape
\Text
\end{document}
4
  • I am not sure what you are aiming to, but you may consider the tcolorboxpackage which is very powerful and versatile.
    – DRi
    Nov 20, 2018 at 8:11
  • @DRi: I haven't found a way to use tcolorbox with a custom \parshape. I need the frame border to be along the parshape, not rectangular. Nov 20, 2018 at 8:13
  • Just not add the tikzpicture at the beginning? Put it after \typesettitle or even cleaner may be inside.
    – Manuel
    Nov 22, 2018 at 9:01
  • @Manuel: I'd prefer to have the \TikzDecorations before any text is placed (see point 2 in the Notes section). Nov 22, 2018 at 16:31

2 Answers 2

11
+500

The problem

The problem is, like in the questions you link to, that using TikZ switches TeX from vertical to horizontal mode. So if your document starts with a TikZ picture, this picture will be part of the first paragraph of your document, which in your case is the title.

When you use \vspace in horizontal mode, this space will be inserted after the current line. Here is a little demonstration:

\documentclass{article}
\usepackage{tikz}

\begin{document}

\fbox{\begin{minipage}[t]{2cm} %% no vspace
    ab cd ef gh ij kl mn op qr st uv wx yz
\end{minipage}}
%
\fbox{\begin{minipage}[t]{2cm} %% vspace in vertical mode
    \vspace{1ex}ab cd ef gh ij kl mn op qr st uv wx yz
\end{minipage}}
%
\fbox{\begin{minipage}[t]{2cm} %% vspace mid paragraph
    ab \vspace{1ex} cd ef gh ij kl mn op qr st uv wx yz
\end{minipage}}
%
\fbox{\begin{minipage}[t]{2cm} %% vspace at the start of a paragraph, in horizontal mode
    \leavevmode\vspace{1ex}ab cd ef gh ij kl mn op qr st uv wx yz
\end{minipage}}
%
\fbox{\begin{minipage}[t]{2cm} %% vspace after a tikz environment
    \begin{tikzpicture}\end{tikzpicture}\vspace{1ex}ab cd ef gh ij kl mn op qr st uv wx yz
\end{minipage}}

\end{document}

output

(Note that the Tikz picture will reside in a paragraph of its own if it is separated from other content by paragraph breaks, in which case it will probably introduce more vertical space than expected or desired.)

A solution

I think the safer way to include an overlay TikZ environment on a given page is to not have it be part of the running text at all and to draw it when the page is being shipped. That way it is guaranteed not to mess up any spacing.

This can be done by using the atbegshi package. The following code is mostly identical to yours: I've added five lines and removed one empty line. The changes are indicated by comments.

\documentclass{article}
\usepackage{showframe}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{layout}

\usepackage{atbegshi} %% <-- I added this line

\usepackage[textwidth=5.0cm]{geometry}

\newcommand*{\Text}{%
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae, felis.%
}%

\newcommand*{\MyAboveSkip}{5pt}
\newcommand*{\MyBotSkip}{5pt}
\newcommand*{\TypesetTitle}[1]{%
    %% See comments in https://tex.stackexchange.com/q/7676/4301
    \hbox{}\kern-\topskip  
    \vspace*{\MyAboveSkip}
    {\bfseries\centering#1\par}%
    \vspace*{\MyBotSkip}%
}%

\NewDocumentCommand{\TikzDecorations}{%
    O{2}% #1 = Number of lines for title
}{%
  \AtBeginShipoutNext{% %% <-- I added this line
    \AtBeginShipoutUpperLeftForeground{% %% <-- I added this line
      \begin{tikzpicture}[remember picture,overlay]
        \coordinate (X) at ([
              xshift=1.0in+\hoffset+\oddsidemargin,
              yshift=-1.0in-\voffset-\topmargin-\headheight-\headsep%
          ]current page.north west);
        % %% <-- Deleted blank line here
        \draw [red] ([yshift=-2\baselineskip-\MyAboveSkip-\MyBotSkip]X) -- ++ (\linewidth,0);
      \end{tikzpicture}%
    }% %% <-- I added this line
  }% %% <-- I added this line
}

\begin{document}%\layout
\TikzDecorations[2]
\sloppy\parskip0pt\relax
\TypesetTitle{A Long Title That Requires Two Lines}%
\noindent\Text

\end{document}

output

Some remarks

  • I had to delete an empty line in your TikZ environment because \AtBeginShipoutNext and \AtBeginShipoutUpperLeftForeground are not defined \long and their arguments are therefore not allowed to contain paragraph breaks. (See e.g. this answer.)

  • The TikZ environment inside \AtBeginShipoutUpperLeftForeground{…} is placed in the top left corner of the page, so it actually has coordinates (0,0). You can therefore replace current page.north west by 0,0 and leave out the remember picture option. The following thus works just as well:

    \NewDocumentCommand{\TikzDecorations}{%
        O{2}% #1 = Number of lines for title
    }{%
      \AtBeginShipoutNext{%
        \AtBeginShipoutUpperLeftForeground{%
          \begin{tikzpicture}[overlay] %% <-- removed remember picture
            \coordinate (X) at ([
                  xshift=1.0in+\hoffset+\oddsidemargin,
                  yshift=-1.0in-\voffset-\topmargin-\headheight-\headsep%
              ]0,0); %% <-- 0,0 instead of current page.north west
            \draw [red] ([yshift=-2\baselineskip-\MyAboveSkip-\MyBotSkip]X) -- ++ (\linewidth,0);
          \end{tikzpicture}%
        }%
      }%
    }
    

    (In fact, you won't even need to run LaTeX twice for this line to show up at the right spot. Note that the opposite corner of the page has coordinates (\paperwidth,-\paperheight), with a minus sign.)

  • If you replace AtBeginShipoutUpperLeftForeground by AtBeginShipoutUpperLeft, then the tikz picture will be drawn behind your text rather than on top of it.

3
  • Works well on the MWE. I made a note of the blank line in my MWE that needed to be deleted in the tikzpicutre for this to compile. Nov 22, 2018 at 17:18
  • Thanks for the replace AtBeginShipoutUpperLeftForeground comment: Using AtBeginShipoutUpperLeft is the right thing for me as I want the drawings on the background. Nov 22, 2018 at 17:58
  • +1: Very nice answer. Looks like a chapter in a tikz book :) Nov 24, 2018 at 7:27
3

You see exactly the same phenomenon if you just insert an empty box instead of \TikzDecorations[2]. I would contest that the problem is the first \vspace* in your \TypesetTitle macro. Indeed

\documentclass[twoside]{article}

\begin{document}
\showoutput
\mbox{}
\clearpage
\vspace*{0pt}
\noindent
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\clearpage
\mbox{}
\vspace*{0pt}
\noindent
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\end{document}

produces text starting at two different locations

Sample demonstration

The simplest solution in your case is to move the \TikzDecorations command to after the title setting command

Sample output

\documentclass{article}
\usepackage{showframe}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{layout}

\newcommand*{\Text}{%
\sloppy\noindent
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae, felis.
Curabitur dictum
gravida mauris. Nam arcu libero, nonummy eget, consectetuer id, vulputate a, magna.
Donec vehicula augue eu neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris ut leo. Cras viverra metus rhoncus sem.
Nulla et lectus vestibulum urna fringilla ultrices.%
}%


\newcommand*{\TitleParShape}{%
    0.40\hsize 0.20\hsize
    0.35\hsize 0.30\hsize
}%
\newcommand*{\BodyParShape}{%
    0.30\hsize 0.40\hsize
    0.25\hsize 0.50\hsize
    0.20\hsize 0.60\hsize
    0.15\hsize 0.70\hsize
    0.10\hsize 0.80\hsize
    0.05\hsize 0.90\hsize
    0.00\hsize 1.00\hsize
}%

\newcommand*{\NumberOfLines}{7 }
\newcommand*{\MyParShape}{%
    \parshape \NumberOfLines
    \BodyParShape
}%

\newcommand*{\MyAboveSkip}{5pt}
\newcommand*{\MyBotSkip}{5pt}
\newcommand*{\TypesetTitle}[1]{%
    %% See comments in https://tex.stackexchange.com/q/7676/4301
    \hbox{}\kern-\topskip
    \vspace*{\MyAboveSkip}
    \parshape 2
    \TitleParShape
    {\bfseries\centering#1\par}%
    \vspace*{\MyBotSkip}%
}%

\NewDocumentCommand{\TikzDecorations}{%
    O{2}% #1 = Number of lines for title
}{%
%    \noindent
    \begin{tikzpicture}[remember picture,overlay]
    \coordinate (X) at ([
            xshift=1.0in+\hoffset+\oddsidemargin,
            yshift=-1.0in-\voffset-\topmargin-\headheight-\headsep%
        ]current page.north west);

        \draw [red] ([yshift=-2\baselineskip-\MyAboveSkip-\MyBotSkip]X) -- ++ (\linewidth,0);
    \end{tikzpicture}%
}

\begin{document}%\layout
\sloppy\parskip0pt\relax
\TypesetTitle{Two Line Title}%
\TikzDecorations[2]
\MyParShape
\Text
\end{document}
1
  • Moving the \TikzDecorations[2] to after the text is placed introduces other complications as per second point in the Notes section. I really would prefer it to be done before any text is placed. Nov 22, 2018 at 16:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .