Linux tip: Creating a pixel ruler from the command line

How to draw lines and text on images with Bash scripting, shell arithmetic, and ImageMagick

Learn how to use the Linux® command line and some basic Bash scripting techniques to draw lines and text on images using ImageMagick. And create a pixel ruler along the way.

Share:

Ian Shields, Senior Programmer, IBM

Ian ShieldsIan Shields works on a multitude of Linux projects for the developerWorks Linux zone. He is a Senior Programmer at IBM at the Research Triangle Park, NC. He joined IBM in Canberra, Australia, as a Systems Engineer in 1973, and has since worked on communications systems and pervasive computing in Montreal, Canada, and RTP, NC. He has several patents and has published several papers. His undergraduate degree is in pure mathematics and philosophy from the Australian National University. He has an M.S. and Ph.D. in computer science from North Carolina State University. Learn more about Ian in Ian's profile on developerWorks Community.


developerWorks Contributing author
        level

23 July 2009

Also available in Japanese Portuguese

Sometimes I need to draw a few lines or some text on an image or a blank canvas. Just recently I needed to include a simple image of a pixel ruler in a developerWorks article. I wanted the image to be 572 pixels wide, to match the current recommended maximum for developerWorks article images as described in "Illustrating your article or tutorial for developerWorks." Lots of on-screen pixel rulers are available for Linux and for Windows (see Resources), but I just wanted a simple GIF, JPEG or PNG image. This tip shows you how to use Bash scripting, shell arithmetic, and ImageMagick to create a pixel ruler.

Make a canvas

The first thing any artist needs is a canvas, so let's create one using the XC pseudo image type in ImageMagick. We also need a color, either one of the many named colors in ImageMagick, or a custom color. (See Resources for a link to the ImageMagick documentation, where you can find a full list of pseudo image types as well as color names.) Listing 1 shows how to make a light blue 572x100 canvas using the convert command. (The convert command is customarily used to convert to a different image format or otherwise alter an image.)

Listing 1. Creating a canvas
convert -size 572x100 xc:lightblue ruler1.gif

Figure 1 shows our new canvas.

Figure 1. A light blue 572x100 pixel canvas for our ruler
A light blue 572x100 pixel canvas for our ruler

Add a few lines

Now that we have a canvas, let's draw marks to show the various points along the ruler. 72 ppi (pixels per inch) is standard for Web-based graphics, so let's put our first mark at 72 pixels and make it extend 30 pixels from the bottom of the image. Let's also make the fill color for the line black and specify the two end points of the line.

Listing 2. Adding a line to the canvas
convert  -fill black -draw "line 72,70 72,100" ruler1.gif ruler2.gif

Figure 2 shows our new image with a single line drawn on it.

Figure 2. The canvas with a single vertical line 72 pixels from the left
The canvas with a single vertical line 72 pixels from the left

Doing this individually for each line we want on the ruler isn't too appealing. So let's use the seq command to generate a list of horizontal offsets 72 pixels apart and a for loop to draw our main lines on the ruler as shown in Listing 3.

Listing 3. Adding lines to the canvas every 72 pixels
convert  -fill black -draw "$(for n in $(seq 0 72 572) ;\
 do echo line $n,70 $n,100 ; done)" ruler1.gif ruler3.gif

Notice that we use two command substitutions to generate the individual line specifications for the draw operation. We also put a mark at 0, the left edge. Figure 3 is now starting to look more like a ruler.

Figure 3. The canvas with vertical lines every 72 pixels from the left
The canvas with vertical lines every 72 pixels from the left

Scripting more lines

At this point, our command line is getting a little complex, and we still have only the major marks on our ruler. Time to start scripting. In Listing 4, we use shell arithmetic and for loops to place a 20-pixel mark half way between the marks we already have, and to place 10-pixel marks every 6 pixels between the longer marks. We'll call the script buildruler.sh and put it in our working directory for the purposes of this article.

Listing 4. Adding a full set of marks to the ruler
#!/bin/bash
# Take user parameters or set defaults
rulername="$1"
rulerlength="$2"
rulername="${rulername:=ruler.gif}"
rulerlength="${rulerlength:=572}"
drawstring=""
#Build the line definitions for the ruler marks
for x1 in `seq 0 72 $rulerlength`; do
  drawstring="$drawstring line $x1,70 $x1,100"
  for x2 in 0 36; do
    (( offset = $x1 + $x2 ))
    drawstring="$drawstring line $offset,80 $offset,100"
    for x3 in `seq 6 6 30`; do
      (( offset2 = $offset + $x3 ))
      drawstring="$drawstring line $offset2,90 $offset2,100"
    done
  done
done
#Create the ruler
convert -size "${rulerlength}x100" xc:lightblue -fill black \
 -draw "$drawstring" "$rulername"

Notice that we added a couple of parameters to allow a user to change the name of the ruler and to specify the length. Our ruler now looks like Figure 4. We used the command ./buildruler.sh ruler4.gif to generate this ruler.

Figure 4. Our ruler with a full set of marks 6 pixels apart
Our ruler with a full set of marks 6 pixels apart

Adding text

Quoting

One of the tricky things with scripting is knowing when quotes are needed and when they are not, and how to pass quoted strings as parameters. Pay particular attention to the quoting used in these scripts.

Now let's label the larger marks on our ruler with the numbers 0, 72, 144, and so on. We need to tell ImageMagick what font to use and what size and color it should be. Obviously, the 0 should be at the left edge of the ruler, but if we place the two- and three-digit numbers at the 72-pixel points, they will appear unbalanced. To correct this, we'll shift them slightly to the left, to make them appear more centered over the mark. Our enlarged script is shown in Listing 5.

Listing 5. Adding labels to the ruler
#!/bin/bash
# Take user parameters or set defaults
rulername="$1"
rulerlength="$2"
rulername="${rulername:=ruler.gif}"
rulerlength="${rulerlength:=572}"
drawstring=""
#Build the line definitions for the ruler marks
for x1 in `seq 0 72 $rulerlength`; do
  drawstring="$drawstring line $x1,70 $x1,100"
  for x2 in 0 36; do
    (( offset = $x1 + $x2 ))
    drawstring="$drawstring line $offset,80 $offset,100"
    for x3 in `seq 6 6 30`; do
      (( offset2 = $offset + $x3 ))
      drawstring="$drawstring line $offset2,90 $offset2,100"
    done
  done
done
#Add the labels
labelfont="-fill black -font helvetica -pointsize 24  -draw"
labelstring="text 0,60 \"0\" "
for x1 in 72; do
  (( offset = $x1 - 12 ))
  labelstring="$labelstring text $offset,60 \"$x1\" "
done
for x1 in `seq 144 72 $rulerlength`; do
  (( offset = $x1 - 18 ))
  labelstring="$labelstring text $offset,60 \"$x1\" "
done

#Create the ruler
convert -size "${rulerlength}x100" xc:lightblue -fill black \
 -draw "$drawstring" $labelfont "$labelstring" "$rulername"

Now our ruler looks like Figure 5.

Figure 5. Our ruler with labels a full set of marks
Our ruler with labels a full set of marks

Positioning text

So how did we know how much to offset the text or how far down from the top to place it? The short answer is that I experimented and guessed! Not an ideal solution.

To do a better job on placing text, we need to know how big the rectangle containing the text will be. But that depends on the font and whether the font is proportional and other things that aren't so easy to calculate. Fortunately, in the same way that we can create a canvas with ImageMagick, we can also create a label. We'll create a title of Pixel Ruler and make it a 36-point font. The label is created as an image, and we can use the identify command to determine its size, as shown in Listing 6.

Listing 6. Creating a title and measuring its size
$ convert -fill NavyBlue -background Lavender -font helvetica -pointsize 36 \
 label:"Pixel Ruler" label.gif
$ identify "label.gif"
label.gif GIF 175x36 175x36+0+0 8-bit PseudoClass 256c 1.98kb

We've made the text navy blue and added a lavender background color to help the image stand out from this page background, but you get the same size image with or without the background color. The resulting label image is shown in Figure 6.

Figure 6. The label as an image
The label as an image

Even better than writing a file, we can use the ImageMagick INFO class to determine even more information about the text, including where the baseline is. We'll write the text onto a large enough canvas and then trim the canvas to the edge of the text as shown in Listing 7.

Listing 7. Using the INFO image class to determine font information
$ convert -size 572x100 xc:lightblue -font helvetica -pointsize 36 \
  -fill black -undercolor lavender  -annotate +40+50 'Pixel Ruler' -trim info:
xc:lightblue XC 174x36 572x100+40+23 16-bit DirectClass

Annotated text starts at a point whose y coordinate represents the baseline of the text. The above output shows that the text fits in a box that is 174x36 pixels in size. The 1-pixel discrepancy between this and our earlier result is not significant. The top of the box is positioned 23 pixels below the top of our original canvas. Since the baseline was 50 pixels below the top of the original canvas, this means the baseline is actually 27 pixels (50 minus 23) below the top of the text, leaving 9 pixels for descenders. Figure 7 illustrates the relationship using an image placed on the untrimmed canvas.

Figure 7. The label metrics
The label metrics

So now that we have the label dimensions, let's place the baseline 40 pixels from the top of the image and center it horizontally. Our final script is shown in Listing 8 and is also available for download.

Listing 8. Our final script
#!/bin/bash
# Take user parameters or set defaults
rulername="$1"
rulerlength="$2"
rulername="${rulername:=ruler.gif}"
rulerlength="${rulerlength:=572}"
drawstring=""
#Build the line definitions for the ruler marks
for x1 in `seq 0 72 $rulerlength`; do
  drawstring="$drawstring line $x1,70 $x1,100"
  for x2 in 0 36; do
    (( offset = $x1 + $x2 ))
    drawstring="$drawstring line $offset,80 $offset,100"
    for x3 in `seq 6 6 30`; do
      (( offset2 = $offset + $x3 ))
      drawstring="$drawstring line $offset2,90 $offset2,100"
    done
  done
done
#Add the labels
labelfont="-fill black -font helvetica -pointsize 24  -draw"
labelstring="text 0,60 '0' "
for x3 in 72; do
  offset3=$(($x3 - 12 ))
  labelstring="$labelstring text $offset3,60 '$x3' "
done
for x4 in `seq 144 72 $rulerlength`; do
  offset4=$(( $x4 - 18 ))
  labelstring="$labelstring text $offset4,60 '$x4' "
done
#Add a title
titledimension=$(convert -size 572x100 xc:lightblue -font helvetica \
  -pointsize 36  -fill black -undercolor lavender\
  -annotate +40+50 'Pixel Ruler' -trim info: | awk ' {print $3 } ')
titlewidth=${titledimension%x*}
titlefont="-fill NavyBlue -font helvetica -pointsize 36"  
titlepos=$(( (($rulerlength - $titlewidth)) / 2 ))
titletext="text $titlepos,30 'Pixel Ruler' "
#Create the ruler
convert -size "${rulerlength}x100" xc:lightblue \
 -fill black  -draw "$drawstring" $labelfont "$labelstring" \
 $titlefont -draw "$titletext" "$rulername"

The final ruler is shown in Figure 8.

Figure 8. The final ruler, complete with title
The final ruler, complete with title

Not too bad for someone with no artistic skills!


A semi-practical example

When images are indented—as in lists, for example—the maximum width allowed on developerWorks is reduced accordingly. So you can use these handy rulers as a check for any images you submit with an article.

  • This is a short ruler in an unordered list that was created with the command ./buildruler.sh ruler9.gif 400.
    Figure 9. A 400-pixel ruler
    A 400-pixel ruler
    • And this is an even shorter ruler that is further indented. It was created with the command ./buildruler.sh ruler10.gif 300.
      Figure 10. A 300-pixel ruler
      A 300-pixel ruler

Conclusion

In this short exercise, you've seen some basic techniques for scripting images containing lines and text using ImageMagick. You'll find more techniques in our articles "Graphics from the command line" and "More graphics from the command line." You'll also find a host of other examples at the ImageMagick home page. See Resources for links.

The scripts here are not bullet-proof. For example, we do not validate that length is a positive number big enough for a meaningful ruler or that the file specified is a valid image file type for ImageMagick. You will find other issues as well. For example, the ruler labels may be truncated as in Figure 10 above, a possibility our script did not consider.

You can also parameterize as many aspects as you wish. Try adding color or ruler height as parameters, for example.

While this tip has focused on using ImageMagick with Linux, ImageMagick is available for other platforms including Windows. Try using these techniques with your favorite scripting tools on your favorite platform.


Download

DescriptionNameSize
Script to build your own pixel rulerbuildruler.zip1KB

Resources

Learn

Get products and technologies

Discuss

  • Get involved in the My developerWorks community; with your personal profile and custom home page, you can tailor developerWorks to your interests and interact with other developerWorks users.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Linux on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux
ArticleID=416343
ArticleTitle=Linux tip: Creating a pixel ruler from the command line
publish-date=07232009