43

In many plain text documents, box drawing characters are used to draw these boxes in figures and tables. Such examples (from RFC 5766) are shown below. Is there a better way to draw this (for instance, a Unix command line tool), other than using the trial-and-error method?


Examples from RFC 5766:

       +----------------------------+---------------------+
       | TURN client to TURN server | TURN server to peer |
       +----------------------------+---------------------+
       |             UDP            |         UDP         |
       |             TCP            |         UDP         |
       |        TLS over TCP        |         UDP         |
       +----------------------------+---------------------+

And this one, also from RFC 5766:

                                        Peer A
                                        Server-Reflexive    +---------+
                                        Transport Address   |         |
                                        192.0.2.150:32102   |         |
                                            |              /|         |
                          TURN              |            / ^|  Peer A |
    Client's              Server            |           /  ||         |
    Host Transport        Transport         |         //   ||         |
    Address               Address           |       //     |+---------+
   10.1.1.2:49721       192.0.2.15:3478     |+-+  //     Peer A
            |               |               ||N| /       Host Transport
            |   +-+         |               ||A|/        Address
            |   | |         |               v|T|     192.168.100.2:49582
            |   | |         |               /+-+
 +---------+|   | |         |+---------+   /              +---------+
 |         ||   |N|         ||         | //               |         |
 | TURN    |v   | |         v| TURN    |/                 |         |
 | Client  |----|A|----------| Server  |------------------|  Peer B |
 |         |    | |^         |         |^                ^|         |
 |         |    |T||         |         ||                ||         |
 +---------+    | ||         +---------+|                |+---------+
                | ||                    |                |
                | ||                    |                |
                +-+|                    |                |
                   |                    |                |
                   |                    |                |
             Client's                   |            Peer B
             Server-Reflexive    Relayed             Transport
             Transport Address   Transport Address   Address
             192.0.2.1:7000      192.0.2.15:50000     192.0.2.210:49191

                                 Figure 1
3

8 Answers 8

49

The free ASCIIflow website will let you draw text boxes, text, lines, arrows, freeform lines, erase, import, export, and even undo/redo. What else would one need?

Here is my wonderful creation using this tool:

+-------------------------------+
|                               |
|  My first ASCII box           |
|                               |
+---------+---------------------+
          |
          |
          |
          | My first ever ASCII arrow
          |
          |
          |
+---------v----------------------+
|                                |
|  My second ASCII box           |
+--------------------------------+
3
  • One of the disadvantages of that website is the clipboard handling. You can't copy/paste directly. You have to do it through buttons on the right side. Jun 5, 2018 at 13:41
  • 5
    Also the 'Download' button turned out to be the import button. And the 'Upload' button turned out the be the export button. Maybe it is me, but that was really confusing.
    – Mixxiphoid
    Jun 5, 2018 at 13:49
  • 1
    @Mixxiphoid No, it isn't just you. And yes, it is really confusing, but works! Jun 6, 2018 at 9:26
20

It is possible to draw such pictures using tools dating back 30 years, namely pic which is part of the troff command suite. These days gnu's groff package will contain the pic command. The link shows a picture of some typical PostScript output, but using nroff or the appropriate options you will get an ascii-art version. See the user manual (pdf) from 1991 for examples.

The tables in your example are probably produced by this same command suite, using just tbl which produces tables from simple lists.

For a gui version, you can use artist-mode in emacs to draw boxes and arrowed lines etc, using the mouse or keyboard. See youtube video demo.

2
  • Isn't pic similar to graphviz's dot?
    – hjpotter92
    Jun 5, 2018 at 8:54
  • Yes. graphviz can output in pic format. I think they are both from the same AT&T Unix background, with graphviz being a special purpose application that got further development, whilst pic gave way to gui-style tools.
    – meuh
    Jun 5, 2018 at 9:08
10

Drawing boxes or other shapes with characters is known as ASCII art (also ANSI or ISO art). There are numerous tools to aid in creating ASCII art, such as online ASCIIFlow, image rendering in ASCII, applications such as figlet, etc. Some have been implemented in JavaScript and can be run in a browser on any OS.

There is nothing new under the sun - micrography is a subset of calligraphy with a long pedigree, used for hundreds of years, using letters to form pictures, such as the calendar below, with much of the image formed from letters.

Omer Calendar, Sotheby's

8

How can I draw ASCII tables?

If you just want to draw ASCII tables (like your first example) see Plain Text Tables generator - TablesGenerator.com for a web utility that will help you create nice data tables:

enter image description here

3

From the command line using terminal-table from GitHub.

Install terminal-table:

gem install terminal-table

For example:

irb
require 'terminal-table'

rows = []
rows << ['UDP', 'UDP']
rows << ['TCP', 'UDP']
rows << ['TLS over TCP ', 'UDP']
table = Terminal::Table.new :headings => ['TURN client to TURN server', 'TURN server to peer'], :rows => rows

puts table

Sample output:

+----------------------------+---------------------+
| TURN client to TURN server | TURN server to peer |
+----------------------------+---------------------+
| UDP                        | UDP                 |
| TCP                        | UDP                 |
| TLS over TCP               | UDP                 |
+----------------------------+---------------------+

The same output can be obtained using python:

pip install terminaltables

for example:

from terminaltables import AsciiTable
table_data = [
    ['TURN client to TURN server', 'TURN server to peer'],
    ['UDP', 'UDP'],
    ['TCP', 'UDP'],
    ['TLS over TCP', 'UDP']
]
table = AsciiTable(table_data)
print table.table
2

Adding to the answer of @meuh I went ahead and created a mini-demo of pic.

$ cat picdemo.roff

.PS
box wid 3 "My first ASCII box"
arrow down 1 from last box.s
box wid 3 "My first ASCII box"
.PE

$ cat picdemo.roff | pic | groff -ms -Tascii

           +-----------------------------+
           |                             |
           |     My first ASCII box      |
           +--------------+--------------+
                          |
                          |
                          |
                          |
                          +
           +--------------+--------------+
           |                             |
           |     My first ASCII box      |
           +-----------------------------+
1

I have this in my .vimrc:

vn<silent> <leader>[ :<c-u>cal<sid>rect(1)<cr>
vn<silent> <leader>] :<c-u>cal<sid>rect(2)<cr>
let s:h=split(' ╶╺╵└┕╹┖┗╴─╼┘┴┶┚┸┺╸╾━┙┵┷┛┹┻╷┌┍│├┝╿┞┡┐┬┮┤┼┾┦╀╄┑┭┯┥┽┿┩╃╇╻┎┏╽┟┢┃┠┣┒┰┲┧╁╆┨╂╊┓┱┳┪╅╈┫╉╋','\zs')
let s:e=map(range(81),'[v:val/27%3,v:val/9%3,v:val/3%3,v:val%3]') "base-3 encode
fu s:rect(x) "x:thickness
 if visualmode()!=#"\<c-v>"|retu|en
 let s=&sel|let&sel='inclusive'|let[ls,cs]=[[line("'<"),line("'>")],[virtcol("'<"),virtcol("'>")]]|let&sel=s
 let[l0,l1,c0,c1]=[min(ls),max(ls),min(cs),max(cs)]
 let a=map(map(getline(l0,l1),"split(v:val,'\\zs')"),"extend(v:val,repeat([' '],max([0,c1-len(v:val)])))")
 let x=a:x|let[V,H]=[[x,0,x,0],[0,x,0,x]] "vertical and horizontal line
 "b:list of changes as [line,column,bitmask]
 if l0<l1&&c0<c1|let b=[[l0,c0,[x,0,0,x]],[l0,c1,[x,x,0,0]],[l1,c0,[0,0,x,x]],[l1,c1,[0,x,x,0]]]
                 let b+=map(range(l0+1,l1-1),'[v:val,c0,V]')+map(range(l0+1,l1-1),'[v:val,c1,V]')
                 let b+=map(range(c0+1,c1-1),'[l0,v:val,H]')+map(range(c0+1,c1-1),'[l1,v:val,H]')
 elsei l0<l1    |let b=[[l0,c0,[x,0,0,0]],[l1,c0,[0,0,x,0]]]+map(range(l0+1,l1-1),'[v:val,c0,V]')
 elsei c0<c1    |let b=[[l0,c0,[0,0,0,x]],[l0,c1,[0,x,0,0]]]+map(range(c0+1,c1-1),'[l0,v:val,H]')
 el             |let b=[]|en
 for[l,c,m]in b
  let i=index(s:h,a[l-l0][c-1])
  if i>=0|let z=map(copy(s:e[i]),'max([v:val,m[v:key]])')|let a[l-l0][c-1]=s:h[27*z[0]+9*z[1]+3*z[2]+z[3]]|en
 endfo
 cal setline(l0,map(a,"join(v:val,'')"))
endf

Selecting a rectangle in block-visual mode (<C-v>) and pressing <leader>[ puts line-drawing characters at its border, merging them with any pre-existing line-drawing characters. If you insist on pure ASCII +-| instead, it should be easy to modify.

1

For all Vim users out there, two venerable oldtimer plugins are available:

  • DrawIt! plugin; this hasn't seen any updates in years, but its author is still active, so this says something about its maturity. It's great for boxes and connecting lines; just what is asked for here. (It can even do circles and ellipses!)
  • sketch.vim is unmaintained, and more mouse-driven, painting-style

You can use both plugins in parallel, just not have both active at the same time.

You must log in to answer this question.

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