Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I am trying to draw a polygon box around a sentence within a paragraph. This box would wrap over multiple lines and terminate exactly at the end of the sentence (i.e. without white space filling the remaining line width). Therefore, the box I'm trying to draw is not a simple rectangle like what is produced by \fbox. I've tried several different approaches:

  1. Using \fbox and \parbox to create a multi-line outline:

    \fbox{\parbox{\linewidth}{%
      Nunc sed pede. Praesent vitae lectus. Praesent neque justo, 
      vehicula eget, interdum id, facilisis et, nibh.}}
    
  2. Using TikZ:

    \newcommand\mybox[2][]{%
      \tikz[overlay]\node[fill=blue!20,inner sep=2pt, 
        anchor=text, rectangle, rounded corners=1mm,#1] {#2};\phantom{#2}}
    

Any help is greatly appreciated.


Here is a simple document that illustrates the above code:

\documentclass{article}
\usepackage{tikz}

\newcommand\mybox[2][]{%
  \tikz[overlay]\node[fill=blue!20,inner sep=2pt, 
    anchor=text, rectangle, rounded corners=1mm,#1] {#2};\phantom{#2}}

\begin{document}

\fbox{\parbox{\linewidth}{Nunc sed pede. Praesent vitae lectus. Praesent 
  neque justo, vehicula eget, interdum id, facilisis et, nibh.}} Phasellus 
  at purus et libero lacinia dictum. Fuscealiquet. Nulla eu ante placerat 
  leo semper dictum. Mauris metus. Curabitur lobortis. Curabitur 
  sollicitudin hendrerit nunc. Donec ultrices lacus id ipsum.

\mybox{Class aptent taciti sociosqu ad litora torquent per conubia nostra, 
  per incepto hymenaeos.} Aenean nonummy turpis id odio. Integer euismod 
  imperdiet turpis. Ut nec leo nec diam imperdiet lacinia. Etiam eget lacus 
  eget mi ultricies posuere. In placerat tristique tortor. Sed porta 
  vestibulum metus. Nulla iaculis sollicitudin pede. Fusce luctus tellus 
  in dolor. Curabitur auctor velit a sem. Morbi sapien. Class aptent taciti 
  sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. 
  Donec adipiscing urna vehicula nunc. Sed ornareleo in leo. In rhoncus leo 
  ut dui. Aenean dolor quam, volutpat nec, fringilla id, consectetuer vel, pede

\end{document}
share|improve this question
1  
Welcome to TeX.SX! Please add a minimal working example (MWE) that illustrates your problem. It will be much easier for us to reproduce your situation and find out what the issue is when we see compilable code, starting with \documentclass{...} and ending with \end{document}. –  Jubobs May 30 '13 at 17:08

2 Answers 2

up vote 17 down vote accepted

Update.

Improved version:

\tikzmark is used to set marks at the beginning and end of the text that will be boxed. The main command is

\BoxedText[<options>]{<start-mark>}{<end-mark>} 

The optional argument can be used to pass options to the \draw command used internally (by default, just a frame is drawn, but you can fill it, or change the line width, or apply any other desired valid modification):

\documentclass{article}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc}

\makeatletter
\tikzset{%
  remember picture with id/.style={%
    remember picture,
    overlay,
    save picture id=#1,
  },
  save picture id/.code={%
    \edef\pgf@temp{#1}%
    \immediate\write\pgfutil@auxout{%
      \noexpand\savepointas{\pgf@temp}{\pgfpictureid}}%
  },
  if picture id/.code args={#1#2#3}{%
    \@ifundefined{save@pt@#1}{%
      \pgfkeysalso{#3}%
    }{
      \pgfkeysalso{#2}%
    }
  }
}

\def\savepointas#1#2{%
  \expandafter\gdef\csname save@pt@#1\endcsname{#2}%
}

\def\tmk@labeldef#1,#2\@nil{%
  \def\tmk@label{#1}%
  \def\tmk@def{#2}%
}

\tikzdeclarecoordinatesystem{pic}{%
  \pgfutil@in@,{#1}%
  \ifpgfutil@in@%
    \tmk@labeldef#1\@nil
  \else
    \tmk@labeldef#1,(0pt,0pt)\@nil
  \fi
  \@ifundefined{save@pt@\tmk@label}{%
    \tikz@scan@one@point\pgfutil@firstofone\tmk@def
  }{%
  \pgfsys@getposition{\csname save@pt@\tmk@label\endcsname}\save@orig@pic%
  \pgfsys@getposition{\pgfpictureid}\save@this@pic%
  \pgf@process{\pgfpointorigin\save@this@pic}%
  \pgf@xa=\pgf@x
  \pgf@ya=\pgf@y
  \pgf@process{\pgfpointorigin\save@orig@pic}%
  \advance\pgf@x by -\pgf@xa
  \advance\pgf@y by -\pgf@ya
  }%
}
\newcommand\tikzmark[2][]{%
\tikz[remember picture with id=#2] #1;}
\makeatother

\newcommand\BoxedText[3][]{%
\begin{tikzpicture}[remember picture,overlay]
\draw[#1] 
  let \p1=(pic cs:#2), \p2=(pic cs:#3) in
  ([yshift=-0.8ex]\p1) --
  ([yshift=2ex]\p1) -- 
  ([xshift=3pt,yshift=2ex]\p1-|current page text area.east) -- 
  ([xshift=3pt,yshift=2ex]\p2-|current page text area.east) --
  ([yshift=2ex]\p2) --
  ([yshift=-0.8ex]\p2) --
  ([xshift=-3pt,yshift=-0.8ex]\p2-|current page text area.west) --
  ([xshift=-3pt,yshift=-0.8ex]\p1-|current page text area.west) --
  cycle
;
\end{tikzpicture}%
}

\begin{document}

\BoxedText{start1}{end1}
\BoxedText[draw=orange!70!black,right color=orange!10,left color=orange!50]{start2}{end2}
\BoxedText[draw=cyan!70!black,fill=cyan!30,ultra thick]{start3}{end3}

Some text goes here.\tikzmark{start1} Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here.\tikzmark{end1} Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here.

Some text goes here. Some text goes here. Some text goes here. Some text goes here.Some text goes here.\tikzmark{start3} Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text Some text goes here.\tikzmark{end3}

Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here.Some text goes here. Some text goes here. Some text goes here. Some text goes here.Some text goes here.\tikzmark{start2} Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here.\tikzmark{end2} Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here. Some text goes here.

\end{document}

enter image description here

The code must be run three times.

share|improve this answer
    
+1. Gonzalo.... –  Harish Kumar May 30 '13 at 23:57
    
@HarishKumar Thank you! –  Gonzalo Medina May 31 '13 at 0:03

I've expanded on @Gonzalo's answer by putting these commands into a class, which keeps the Tex file a little bit cleaner.

 \ProvidesPackage{edit-tools}
 \RequirePackage{tikzpagenodes}
 \usetikzlibrary{calc}

 \tikzset{%
    remember picture with id/.style={%
    remember picture,
    overlay,
    save picture id=#1,
 },
 save picture id/.code={%
   \edef\pgf@temp{#1}%
   \immediate\write\pgfutil@auxout{%
     \noexpand\savepointas{\pgf@temp}{\pgfpictureid}}%
   },
   if picture id/.code args={#1#2#3}{%
      \@ifundefined{save@pt@#1}{%
          \pgfkeysalso{#3}%
       }{
          \pgfkeysalso{#2}%
        }
    }
 }

 \def\savepointas#1#2{%
   \expandafter\gdef\csname save@pt@#1\endcsname{#2}%
 }

 \def\tmk@labeldef#1,#2\@nil{%
   \def\tmk@label{#1}%
   \def\tmk@def{#2}%
 }

 \tikzdeclarecoordinatesystem{pic}{%
   \pgfutil@in@,{#1}%
   \ifpgfutil@in@%
     \tmk@labeldef#1\@nil
   \else
     \tmk@labeldef#1,(0pt,0pt)\@nil
   \fi
   \@ifundefined{save@pt@\tmk@label}{%
     \tikz@scan@one@point\pgfutil@firstofone\tmk@def
   }{%
   \pgfsys@getposition{\csname save@pt@\tmk@label\endcsname}\save@orig@pic%
   \pgfsys@getposition{\pgfpictureid}\save@this@pic%
   \pgf@process{\pgfpointorigin\save@this@pic}%
   \pgf@xa=\pgf@x
   \pgf@ya=\pgf@y
   \pgf@process{\pgfpointorigin\save@orig@pic}%
   \advance\pgf@x by -\pgf@xa
   \advance\pgf@y by -\pgf@ya
   }%
 }
 \newcommand\tikzmark[2][]{%
 \tikz[remember picture with id=#2] #1;}

 \newcommand\BoxedText[3][]{%
 \begin{tikzpicture}[remember picture,overlay]
 \draw[#1] 
   let \p1=(pic cs:#2), \p2=(pic cs:#3) in
   ([yshift=-0.8ex]\p1) --
   ([yshift=2ex]\p1) -- 
   ([xshift=3pt,yshift=2ex]\p1-|current page text area.east) -- 
   ([xshift=3pt,yshift=2ex]\p2-|current page text area.east) --
   ([yshift=2ex]\p2) --
   ([yshift=-0.8ex]\p2) --
   ([xshift=-3pt,yshift=-0.8ex]\p2-|current page text area.west) --
   ([xshift=-3pt,yshift=-0.8ex]\p1-|current page text area.west) --
   cycle
 ;
 \end{tikzpicture}%
 }

\newcommand{\gradientbox}[2]{%
     \BoxedText[draw=orange!70!black,right color=orange!10,left color=orange!50]     {start#1}{end#1}
     \tikzmark{start#1}#2
     \tikzmark{end#1}
 }

\newcommand{\shadedbox}[2]{%
\BoxedText[draw=cyan!70!black,fill=cyan!30,ultra thick]{start#1}{end#1}
\tikzmark{start#1}#2
\tikzmark{end#1}
 }

\newcommand{\outlinebox}[2]{%
     \BoxedText{start#1}{end#1}
     \tikzmark{start#1}#2
     \tikzmark{end#1}
 }

This can be used by calling one of the three commands \gradientbox, \shadedbox, or \outlinebox and passing it a unique id. For example:

 Some text goes here. \gradientbox{g1}{Some text goes here. Some text goes here. Some 
 text goes here. Some text goes here. Some text goes here.} Some text goes here. Some 
 text goes here. Some text goes here. Some text goes here. Some text goes here. Some
 text goes here. Some text goes here. Some text goes here. Some text goes here. Some
 text goes here. Some text goes here.    
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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