83

I'm trying to make a simple directory tree representation in LaTeX. Something like:

spam
 |
 +-- ham
 +-- eggs
     |
     +-- more spam
     +-- dead parrots

The tree won't be that long (say, 10--15 entries or so), with three or so levels, and is a one-off thing. So, quick-and-dirty solutions are welcome. When recommending tikz1, please note that I have almost no experience with it, so I'd appreciate a clean simple solution, even if it doesn't look quite perfect.

1 On Tex.SE tikz is almost like the jquery recurring joke on StackOverflow..


Edit: As per Thorsten's suggestion, dirtree is a nice and easy-to-use solution. The example from above could be written as:

\usepackage{dirtree}
...
\dirtree{%
.1 spam.
.2 ham.
.2 eggs.
.3 more spam.
.3 dead parrots.
}
47

The dirtree package is your friend.

  • 10
    Unfortunately dirtree is in conflict with algorithmic and pseudocode. So it's useless in many cases. – user6052 Jun 9 '11 at 12:11
74
+50

Here's one option using the powerful forest package; since its built upon TikZ the customization possibilities are enormous.

For example one can easily simulate the style produced by dirtree:

\documentclass[border=5pt]{standalone}
\usepackage{forest}

\begin{document}

\begin{forest}
  for tree={
    font=\ttfamily,
    grow'=0,
    child anchor=west,
    parent anchor=south,
    anchor=west,
    calign=first,
    edge path={
      \noexpand\path [draw, \forestoption{edge}]
      (!u.south west) +(7.5pt,0) |- node[fill,inner sep=1.25pt] {} (.child anchor)\forestoption{edge label};
    },
    before typesetting nodes={
      if n=1
        {insert before={[,phantom]}}
        {}
    },
    fit=band,
    before computing xy={l=15pt},
  }
[text1
  [text1.1
    [text1.1.1]
    [text1.1.2]
    [text1.1.3]
  ]
  [text1.2
    [text1.2.1]
    [text1.2.2]
  ]
]
\end{forest}

\end{document}

enter image description here

And a simple modification to the previous specifications

  for tree={
    font=\sffamily,
    text=white,
    text width=2cm,
    minimum height=0.75cm,
    if level=0
      {fill=mygreenii}
      {fill=mygreeni},
    rounded corners=4pt,
    grow'=0,
    child anchor=west,
    parent anchor=south,
    anchor=west,
    calign=first,
    edge={mygreenii,rounded corners,line width=1pt},
    edge path={
      \noexpand\path [draw, \forestoption{edge}]
      (!u.south west) +(7.5pt,0) |- (.child anchor)\forestoption{edge label};
    },
    before typesetting nodes={
      if n=1
        {insert before={[,phantom]}}
        {}
    },
    fit=band,
    s sep=15pt,
    before computing xy={l=15pt},
  }

now gives:

enter image description here

The initial theme can also be easily changed to

\documentclass[border=5pt]{standalone}
\usepackage{forest}

\definecolor{folderbg}{RGB}{124,166,198}
\definecolor{folderborder}{RGB}{110,144,169}

\def\Size{4pt}
\tikzset{
  folder/.pic={
    \filldraw[draw=folderborder,top color=folderbg!50,bottom color=folderbg]
      (-1.05*\Size,0.2\Size+5pt) rectangle ++(.75*\Size,-0.2\Size-5pt);  
    \filldraw[draw=folderborder,top color=folderbg!50,bottom color=folderbg]
      (-1.15*\Size,-\Size) rectangle (1.15*\Size,\Size);
  }
}

\begin{document}

\begin{forest}
  for tree={
    font=\ttfamily,
    grow'=0,
    child anchor=west,
    parent anchor=south,
    anchor=west,
    calign=first,
    inner xsep=7pt,
    edge path={
      \noexpand\path [draw, \forestoption{edge}]
      (!u.south west) +(7.5pt,0) |- (.child anchor) pic {folder} \forestoption{edge label};
    },
    before typesetting nodes={
      if n=1
        {insert before={[,phantom]}}
        {}
    },
    fit=band,
    before computing xy={l=15pt},
  }  
[system
  [config
  ]
  [lib
    [Access
    ]
    [Plugin
    ]
  ]
  [templates
  ]
  [tests
  ]
]
\end{forest}

\end{document}

which produced

enter image description here

And combining these settings with a variation of my answer to Draw directory tree using folder icons one can produce

\documentclass[border=10pt]{standalone}
\usepackage{forest}

\definecolor{fblue}{RGB}{92,144,192}
\definecolor{fgreen}{RGB}{34,162,70}

\newcommand\myfolder[2][fblue]{%
\begin{tikzpicture}[overlay]
\begin{scope}[xshift=20pt]
\filldraw[rounded corners=1pt,fill=#1,draw=white,double=black]
  (-23pt,10pt) -- ++(3pt,5pt) -- ++(18pt,0pt) -- ++(40:3pt) -- ++(9pt,0pt) -- ++(-40:3pt)
  -- (20pt,15pt) -- (23pt,10pt) -- cycle;
\filldraw[rounded corners,draw=white,double=black,top color=#1,bottom color=#1!30]
  (-22pt,-12pt) -- ++(44pt,0pt) -- (25pt,12pt) coordinate (topr) -- ++(-50pt,0pt) coordinate (topl) -- cycle;
\end{scope}  
\end{tikzpicture}%
\makebox[35pt]{\raisebox{-3pt}{{\ttfamily/#2}}}%
}

\begin{document}

\begin{forest}
  for tree={
    font=\sffamily,
    minimum height=0.75cm,
    rounded corners=4pt,
    grow'=0,
    inner ysep=8pt,
    child anchor=west,
    parent anchor=south,
    anchor=west,
    calign=first,
    edge={rounded corners},
    edge path={
      \noexpand\path [draw, \forestoption{edge}]
      (!u.south west) +(12.5pt,0) |- (.child anchor)\forestoption{edge label};
    },
    before typesetting nodes={
      if n=1
        {insert before={[,phantom,minimum height=18pt]}}
        {}
    },
    fit=band,
    s sep=12pt,
    before computing xy={l=25pt},
  }
[\myfolder{}
  [\myfolder{bin}]
  [\myfolder{dev}]
  [{\myfolder[fgreen]{home}}
    [{\myfolder[fgreen]{anna}}]
    [{\myfolder[fgreen]{tess}}]
  ]
  [\myfolder{media}]
  [\myfolder{mnt}]
  [\myfolder{usr}]
]
\end{forest}

\end{document}

enter image description here

What should be noticed here is that once the basic specification for the vertical structure has been established (which I borrowed with some modifications from cfr (See, for example her answer to how to adjust large number of nodes in forest environment?)):

  for tree={
    grow'=0,
    child anchor=west,
    parent anchor=south,
    anchor=west,
    calign=first,
    edge path={
      \noexpand\path [draw, \forestoption{edge}]
      (!u.south west) +(7.5pt,0) |- node[fill,inner sep=1.25pt] {} (.child anchor)\forestoption{edge label};
    },
    before typesetting nodes={
      if n=1
        {insert before={[,phantom]}}
        {}
    },
    fit=band,
    before computing xy={l=15pt},
  }

all one has to do is to let the imagination fly and produce as many variations as desired using the power of forest/TikZ.

  • Awesome answer. Just out of curiosity, is this a new package? Cause the question is ~5 years old. – Some guy Oct 2 '15 at 18:41
  • 2
    @Rakesh I have started a bounty on the question to draw some attention to it. My motivation for the bounty is that a recent question was closed as a duplicate of this one, but I didn't find the answers here satisfactory. – yo' Oct 2 '15 at 18:43
  • @yo' What other kind of variation/customization would you like to see? – Gonzalo Medina Oct 2 '15 at 18:50
  • @GonzaloMedina I like it as a proof of concept. Once it's clear you can easily do it with forest, everybody can use any customization this package offers to provide the result. Awesome answer! – yo' Oct 2 '15 at 18:52
  • @GonzaloMedina Does it have any 'folder' structure (like the last one you added) that is little bit subtler. Like something that we see in 'Windows Explorer' and in 'Mac's Finder'? I mean to ask, the text follows the icon, and not inside that – Some guy Oct 2 '15 at 18:54
7

Following @cfr's lead: a rewrite of one of Gonzalo Medina's trees but with an option to put files inside the tree as well. Just call the option is file for the file nodes. Also the folder pic now takes one argument which is the folder icon size, making possible to emphasize a specific folder by calling this folder size=<dim>. The key folder tree indent=<dim> sets how much the icon goes inside the tree and folder indent=<dim> sets how much after the icon the line comes from.

The style folder icons (or folder icons=<dim>) is defined on top of the folder style from forest, the only change made to it was the edge path (a pic was placed at the end). Important: As mentioned by @cfr in the comments using it without grow'=0 leads to unexpected results. That is due to the fact that the icon is placed at the end of the edge causing the icon and text to overlap, to fix this some extra space inner xsep is automatically added by folder icons, but if the tree grows vertically than the xsep needs to become ysep. Do note that using just sep would do for both cases but the edge would be too far away from the text making it not so pretty...

\documentclass[tikz,border=5mm]{standalone}
\usepackage[edges]{forest}

\definecolor{foldercolor}{RGB}{124,166,198}

\tikzset{pics/folder/.style={code={%
    \node[inner sep=0pt, minimum size=#1](-foldericon){};
    \node[folder style, inner sep=0pt, minimum width=0.3*#1, minimum height=0.6*#1, above right, xshift=0.05*#1] at (-foldericon.west){};
    \node[folder style, inner sep=0pt, minimum size=#1] at (-foldericon.center){};}
    },
    pics/folder/.default={20pt},
    folder style/.style={draw=foldercolor!80!black,top color=foldercolor!40,bottom color=foldercolor}
}

\forestset{is file/.style={edge path'/.expanded={%
        ([xshift=\forestregister{folder indent}]!u.parent anchor) |- (.child anchor)},
        inner sep=1pt},
    this folder size/.style={edge path'/.expanded={%
        ([xshift=\forestregister{folder indent}]!u.parent anchor) |- (.child anchor) pic[solid]{folder=#1}}, inner ysep=0.6*#1},
    folder tree indent/.style={before computing xy={l=#1}},
    folder icons/.style={folder, this folder size=#1, folder tree indent=3*#1},
    folder icons/.default={12pt},
}

\begin{document}
  \begin{forest}
    for tree={font=\sffamily, %grow'=0,
    folder indent=.9em, folder icons,
    edge=densely dotted}
    [main folder
      [images, this folder size=20pt
          [wallpapers]
          [logo.pdf, is file]]
      [tex-files
          [chapter1.tex, is file]]
      [main.tex, is file]
      [main.aux, is file]
    ]
  \end{forest}
\end{document}

With grow'=0

Now removing the grow'=0 and replacing xsep with ysep (there's only one instance of this in the code - also folder indent needs to be adjusted):

Vertical growth - needs ysep instead of xsep

The main.tex too close to the edge is because fit is set to tight, setting it to band fixes it but the tree grows larger...

  • Nice! I'm not sure how robust the edge path will be with .expanded. (I'm not sure it won't be, either.) Did you try \foresteregister instead? – cfr Nov 23 '16 at 16:18
  • @cfr, I consulted the manual before any trickery was done. That's actually exactly what the folder style provided by forest does (among other things of course). So I didn't gave any thought to it, just copied pasted. ;P – Guilherme Zanotelli Nov 23 '16 at 16:23
  • Fair enough. Though folder itself has some odd effects. But you can blame Sašo in this case :-). – cfr Nov 23 '16 at 18:44
  • If the tree grows in the default direction, something goes very wrong! That is, try commenting out grow'=0 ... – cfr Nov 23 '16 at 23:14
  • Indeed, I've managed to isolate the issues. The weird big spaces between lines and folders is because of the big value I used for folder indent tree (mainly for demonstration). The folder text overlapping with the folder Icon was because I used xsep, using sep instead fixes it but the edges get backed away too, the main Problem in that matter is the folder Icon, it's placed at the end of the edge and if no inner spaced is added the Icon overlaps with the text... I changed some things and added Information about this in the answer. If you find anything else do say! =D – Guilherme Zanotelli Nov 24 '16 at 7:42
7

Rewrite of one of Gonzalo Medina's trees above for Forest v2 which makes it much easier to draw this kind of tree.

Updated versions of two of the other styles: https://tex.stackexchange.com/a/361248/ and https://tex.stackexchange.com/a/328987/.

\documentclass[tikz,border=10pt,multi,dvipsnames,svgnames,x11names]{standalone}
\usepackage[edges]{forest}

\begin{document}
\begin{forest}
  my label/.style={
    label={[font=\sffamily]right:{#1}},
  },
  for tree={% addaswyd o gôd Gonzalo Medina: https://tex.stackexchange.com/a/270761/
    folder,
    font=\sffamily,
    text=white,
    minimum height=0.75cm,
    text width=20mm,
    if level=0{fill=ForestGreen}{fill/.wrap pgfmath arg={SlateBlue#1}{int(4-(mod((level()-1),4)))}},
    rounded corners=4pt,
    grow'=0,
    edge={ForestGreen,rounded corners,line width=1pt},
    fit=band,
  },
  [text1
    [text1.1
      [text1.1.1
        [text1.1.1.1
          [text1.1.1.1.1]
          [text1.1.1.1.2]
          [text1.1.1.1.3]
        ]
      ]
      [textt1.1.2
        [text1.1.2.1]
      ]
      [text1.1.3
        [text1.1.3.1]
      ]
    ]
    [text1.2
      [text1.2.1]
      [text1.2.2]
    ]
  ]
\end{forest}
\end{document}

update for v2 of GM's tree

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.