I'm new to TikZ, and I'm trying to solve a problem which should be easy, I found many related examples but none helped me.

Given the following MWE:

\tikzstyle{every node}=[font=\small]
\draw plot[only marks, mark=+] coordinates{(-23.3mm,  18.6mm) (-23.3mm,  10.8mm) (-23.3mm,  2.3mm ) (-23.3mm, -5.9mm ) (-23.3mm, -14.2mm) (-23.3mm, -14.2mm) (-23.3mm, -22.2mm)};
\draw (-23.3mm,  18.6mm) node[anchor=west, text width=60.3mm ]{A. This is quite short.};
\draw (-23.3mm,  10.8mm) node[anchor=west, text width=60.3mm ]{B. This is already a bit longer, it should be on two lines.};
\draw (-23.3mm,  2.3mm) node[anchor=west, text width=60.3mm ]{C. This is really the longest of all blocks and it will span a whole number of three lines!!!};
\draw (-23.3mm,  -5.9mm) node[anchor=west, text width=60.3mm ]{D. This is short};
\draw (-23.3mm,  -14.2mm) node[anchor=west, text width=60.3mm ]{E. This is long but not too much but should again span two lines.};
\draw (-23.3mm,  -22.2mm) node[anchor=west, text width=60.3mm ]{F. This is short.};

I would like

  1. the text blocks to be as aligned as possible to the + anchors, using anchor=west.
  2. a minimum vertical space between blocks, which overrides the previous condition: if necessary, adjacent blocks should be displaced vertically to make room for three-lines blocks.

The preferred anchors positions, text width, image size and font are all fixed.

In the example, B should be moved up a bit, to make room for C.

The issue is that I want to automate this over several images, with different sets of text blocks, and I do not know in advance whether/where the three-line blocks will be (however I can make sure I do not have neighboring three-line blocks).

Note that if all blocks had three lines I would get out of the margins, so I cannot simply set a minimum height for all blocks. However I'm sure I have 6 blocks per image, only 1 or 2 are long, and they're not adjacent.

Note: I'm using XeLaTeX

Feel free to guess what I'm trying to do :)

  • use the examples given in the positioning library in the manual.
    – percusse
    May 31 '13 at 20:44
  • For the example you give the easiest way is just to set the text as normal paragraphs without using tikz at all. You could do that and then use tikz to overlay nodes over the natural position of the paragraphs. Is there an overriding need to set it as a single tikzpicture in your real code? May 31 '13 at 20:47
  • @DavidCarlisle but how can I make sure that the blocks are close to the anchors? E.g. if I have an image with 6 one-liners. About the overriding need: everything will be overlayed to a picture, so I figured doing all in tikz would be easier..
    – ggll
    May 31 '13 at 20:52
  • oh I misunderstood I think. So the + marks are fixed and you want to position text as close to them as possible. @percusse (who has the advantage over me of knowing something about tikz) suggests there is an existing library for that, so I'll pass on thi, but otherwise the method of this answer might be applicable tex.stackexchange.com/questions/78193 May 31 '13 at 20:58

If I understand correctly the problem, you need to have the + symbols evenly spaced, and the text "as near as possible" to each +, but avoiding the paragraphs being too close. So you allow for small shifts of particular paragraphs, leaving the others untouched.

So, an acceptable output would be:

Expected result

I'll draw a box around each paragraph to clarify. As you see, the paragraph B was shifted and the + is not longer vertically centered in this paragraph. But distance among + is constant.

Boxed result

How it was done

As you guessed, this required two passes. In the first pass all paragraphs are drawn as rectangluar nodes, vertically centered with its corresponding + symbol, but they were typeset with white ink, so they are not visible.

In the second pass, the distance between each node and the next one is measured. If the distance is below 1mm, the node is raised 1.5mm. I've found this heuristic to produce good results.

In order to simplify the coding of the figure, all the texts are saved in a list, which is reused in each pass.

\usetikzlibrary{calc, positioning}

% Define the list with the texts:
 {A. This is quite short.},
 {B. This is already a bit longer, it should be on two lines.},
 {C. This is really the longest of all blocks and it will span a whole number of three lines!!!},
 {D. This is short},
 {E. This is long but not too much but should again span two lines.},
 {F. This is short.}

  paragraph/.style = {
    inner sep=0pt,
    text width=60.3mm

% First draw the + symbols, evenly spaced spawning a distance of 4cm
% This distance is harcoded because it is the one you used in your
% example.
\coordinate(first) at (0,0);
\coordinate (last) at (0,-4cm);
% The + are at 0%, 20%, 40%, 60%, 80% and 100% of that distance
\foreach[count=\i] \f in {0,0.2,...,1} 
   {\node[inner sep=0pt] (item\i) at ($(first)!\f!(last)$) {\tiny +}; }

% First pass (invisible nodes)
\foreach[count=\i] \text in \listoftexts
   \node[paragraph, anchor=west, right=0.4mm of item\i.east, white] (item\i) {\text};

% A little cheat, fake 7-th node
\coordinate (item7) at ($(item6)+(0,5mm)$);

% Second pass, adjust distances
\foreach[count=\i] \text in \listoftexts {
\path let
  in   node[paragraph] at ($(item\i)+(0,\n1)$) {\text};

In the next figure, I've drawn in pink the nodes in the first pass, so you can see the original position of the nodes and how much they were shifted.



You can still use the positioning library if you are overlaying these nodes. Basic mechanism is the fix the first node somewhere and place the rest of the nodes using the

below=###mm of <the node above it>

option. I don't know what the underlying figure looks like but here is a general template.


\begin{tikzpicture}[node distance=0.5cm and 1cm,%Set up the vertical and the horizontal spacing
myblock/.style={draw,text width=60.3mm,label={left:+}},%Create common settings for your nodes and add a + label to the left
\node[myblock] (nodeA) {A. This is quite short.}; %Start with the first node and keep placing nodes OR use chain library(see manual)
\node[myblock,below= of nodeA] (nodeB) {B. This is already a bit longer, it should be on two lines.This is short};
\node[myblock,below= of nodeB] (nodeC) {C. This is really the longest of all blocks and it will span a whole number of three lines!!!};
\node[myblock,below= of nodeC] (nodeD) {D. This is short};
\node[myblock,below= of nodeD] (nodeE) {E. This is long but not too much but should again span two lines.};
\node[myblock,below= of nodeE] (nodeF) {F. This is short};

You can see that we can omit the dimension in the below=... syntax and then it will assume the distance given by the node distance option.

enter image description here

  • Thanks a lot, but the issue is, I cannot use a fixed distance among the boxes, nor can I move the +. The distance should depend on the size of the boxes. I really think I cannot do this without at least two passes.. I'll try to implement the idea of @DavidCarlisle instead, using \tikzmark. Unless of course there's a better idea around?
    – ggll
    Jun 1 '13 at 14:51
  • Update: @DavidCarlisle example is really too hard for me..
    – ggll
    Jun 1 '13 at 19:45
  • @ggll Instead trial and error I think it's best to put your real case because I can't understand what your problem is.
    – percusse
    Jun 1 '13 at 23:24
  • I ended up cowardly avoiding the problem altogether by reducing font size, now I can fit three-line blocks without having to move neighboring blocks..
    – ggll
    Jun 7 '13 at 16:09

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.