An automatic guitar tablature generator, part 2

Last month, I presented the code for a blank guitar tablature page generator. This becomes much more interesting if it can also render tablature based on user input. As I mentioned last month, there are a lot of "ASCII-art" tablature transcriptions available for download on the internet — it would be cool to automatically parse those and turn those into print-ready tablature, along with the musical notation above it as in professional tablature.

I won't present a full ASCII-art tablature parser here, but sort of a middle-of-the-road utility that takes as input triplets indicating the guitar string (E,A,D,G,B or e), the fret that that string should be played on and the duration of the note. So, for instance, the opening riff to Guns and Roses "Sweet Child O' Mine" would be input as:

  • 8,D,12
  • 8,B,14
  • 8,B,12
  • 8,D,12
  • 8,e,15
  • 8,G,13
  • 8,e,14
  • 8,G,13
Where the first entry gives the frequency (i.e. 1/8th or eighth note), followed by the string name, and finally the numeric fret. Chords can be represented by additional strings and frets.

These I'll encapsulate in a new class named Note along with a couple of Enums that represent guitar strings and note durations as shown in listing 1:

enum GuitarString  {
  E(0,"E"),A(1,"A"),D(2,"D"),G(3,"G"),B(4,"B"),e(5,"e");

  public int offset;
  public String name;

  GuitarString(int offset, String name)  {
    this.offset = offset;
    this.name = name;
  }

  public String toString()  {
    return name;
  }
};

enum Duration  {
  whole(1), half(2), quarter(4), eighth(8), sixteenth(16);

  public int frequency;

  Duration(int frequency)  {
    this.frequency = frequency;
  }
};

class Note  {
  GuitarString str;
  int fret;
  Duration dur;

  public Note(GuitarString str, int fret, Duration dur)  {
    this.str = str;
    this.fret = fret;
    this.dur = dur;
  }
  ...

Listing 1: Note class declaration

Given the string and the fret number, it's an easy matter to output the tablature. Recall from last month that the tablature section is six lines (one for each guitar string) of height tabGap — since a guitar string can report its "offset" relative to the low E-string, rendering the tablature of a note is a simple multiplication as shown in listing 2.

  public void renderTab(PdfContentByte canvas, float x, float y)  {
    float offset = str.offset * TabWriter.tabGap;
    canvas.showTextAligned(PdfContentByte.ALIGN_LEFT, Integer.toString(fret), 
      x, y - 2 + offset, 0);
  }

Listing 2: rendering tablature

renderTab accepts as input a PdfContentByte instance from the iText library as I discussed last month. This is the same object that is responsible for implementing the line-drawing primitives that output the tablature lines themselves. showTextAligned is a new method, though. Although its behavior is pretty self-explanatory, it will maddeningly output nothing unless boxed in between a pair of beginText/endText invocations. So, given an array of Note objects, assuming that the tablature lines have already been output as discussed in part 1, the tab can be inserted into the right place as shown in listing 3:

    canvas.beginText();
    BaseFont bf = BaseFont.createFont(BaseFont.COURIER, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
    canvas.setFontAndSize(bf, 8.0F);

    y = pageSize.getHeight() - marginTop - staffHeight;
    x = marginLeft + clefWidth;
    int measureCount = 0;

    for (Note note : measure)  {
      note.renderTab(canvas, x, y);
      x += noteWidth;
    }

    canvas.fillStroke();
    canvas.endText();

Listing 3: rendering tablature

This should be inserted right between the canvas.stroke() call and the document.close() invocations from example 4 of last month's post. Of course, all by itself, this isn't that interesting — hardly better than the ASCII-art representations you can find all over the internet. To be complete, I should compute the correct positioning and rendering of the musical note that corresponds to this tablature note. This turns out to be computationally quite a bit more complex.

First, to make the math easier, I "normalize" everything onto a single, very long, theoretical low-E string and then compute the position of the musical note from there. This is possible because each string on a guitar is strung (in standard tuning) two-and-a-half steps below the one above it, so the pitches repeat if you move up and to the right, as shown in figure 1.

Figure 1: guitar notes repeating

This would then translate to a simple multiplication: the open E-string, fret 0, is an E; the next two are F and F#, the next two after that are G and G#, followed by A and A#, etc. So I could almost multiply the fret number by the height of a music note, then divide by two (to account for the fact that each note on the musical staff appears at the same height as its "sharped" counterpart), and render the note there. This would work, if not for the fact that E and B don't have sharped notes. Thus, to find the compute the correct place to render the music note, I first adjust for the "missing" sharped notes and then multiply. I do this in three separate places, for three separate reasons: first, to determine where to place the note itself. Second, to determine whether or not to draw connecting bars up into the standard staff, and third, to determine if the note should be rendered with a sharp (#) marker.

On a 24-fret guitar (the "longest" guitar I've ever seen), this works out to 48 "virtual" frets, whose notes are named as illustrated in the table below.

EADGBeNote
0-----E
1-----F
2-----F#
3-----G
4-----G#
50----A
61----A#
72----B
83----C
94----C#
1050---D
1161---D#
1272---E
1383---F
1494---F#
151050--G
161161--G#
171272--A
181383--A#
1914940-B
20151051-C
21161162-C#
22171273-D
23181384-D#
241914950E
2520151061F
2621161172F#
2722171283G
2823181394G#
29241914105A
30252015116A#
31262116127B
32272217138C
33282318149C#
342924191510D
353025201611D#
363126211712E
373227221813F
383328231914F#
393429242015G
403530252116G#
413631262217A
423732272318A#
433833282419B
443934292520C
454035302621C#
464136312722D
474237322823D#
484338332924E

The lowest (pitch) note that you can play on a guitar string is a full octave below the low E that appears at the bottom of the standard treble staff. So, given the fret of my now-normalized string, I want to multiply that by half-a-staff height (remember that musical notes appear both on and between the lines of the staff — that's why I was careful to define the staff height as an even number!) for every other fret; G and G# both appear in the same place, but G# has a sharp-marker on it. But each time I encounter an E or a B, I do want to jump ahead right afterwards. This is easier to visualize if you look at the table below:
fretoffset 1offset 2final offsetnote
0000E
1001F
2002F#
3003G
4004G#
5005A
6006A#
7007B
8109C
91010C#
101011D
111012D#
121013E
131115F
141116F#
151117G
161118G#
171119A
181120A#
191121B
202123C
212124C#
...
This works out to the semi-complex formula:

int adjustedFret = normalizedFret + ((normalizedFret + 5) / 12) + ((normalizedFret + 12) / 12);
In other words, add one step every 12 notes starting with the 5th, and add another every 12 notes starting with the 12th. This, when divided by two and then multiplied by one-half of the height of a staff marker, tells me how many pixels to move up past the lowest renderable note in order to render the current note.

This looks like it could be simplified with a little bit of algebra, but it actually relies on the specific rounding behavior of the two division operations, so won't work unless I leave it as is.

NOW, given the position of the 0 fret (the low E one octave below the lowest on the treble clef), finding the correct spot to draw the musical note is just a simple multiplication:

  float note_y = y + ((adjustedFret / 2) * (TabWriter.staffGap /2));
Where y is the vertical coordinate of the lowest note that can be output (again, the open low E-string).

Modern music notation indicates the duration of each note pictorially; whole and half notes are written out as empty circles, quarter, eighth and sixteenth (and beyond) as filled circles. Further, every note except a whole note has a "stem" and every subdivision below an quarter note adds an extra "flag" as show in listing 4.

  canvas.ellipse(x, note_y, x + 7.0f, note_y + 5.0f);
  if (dur.frequency > 1) {
   if (normalizedFret < 19) {
    canvas.moveTo(x + 7.0f, note_y + 3);
    canvas.lineTo(x + 7.0f, note_y + 3 + (TabWriter.staffGap * 2.5f));
   } else {
    canvas.moveTo(x, note_y + 3);
    canvas.lineTo(x, note_y + 3 - (TabWriter.staffGap * 2.5f));
   }
   if (dur.frequency > 4) {
    // one flag
    if (normalizedFret < 19)  {
      canvas.rectangle(x + 7.0f, note_y + 3 + (TabWriter.staffGap * 2.0f), 8, 4);
    } else  {
      canvas.rectangle(x - 8.0f, note_y - (TabWriter.staffGap * 2.0f), 8, 4);
    }
    if (dur.frequency > 8) {
     // two flags
      if (normalizedFret < 19)  {
        canvas.rectangle(x + 7.0f, note_y + 3 + (TabWriter.staffGap * 1.0f), 8, 4);
      } else  {
        canvas.rectangle(x - 8.0f, note_y - (TabWriter.staffGap * 1.0f), 8, 4);
      }
    }
   }
  }

Listing 4: rendering musical notes

However, this doesn't quite render correctly, at least for the notes that appear above and below the treble clef (which, on a guitar, are more than half of them). Those notes should be connected to the staff with leading lines as illustrated in figure 2.

Figure 2: leader lines

Since I computed the y position of the note in listing 3, it's easy enough to count down or up from the top or the bottom of the staff to it, adding lines as I go:

    int staffBottom = (int) (y + (TabWriter.staffGap * 3));
    int staffTop = (int) (staffBottom + (TabWriter.staffGap * 5));

    int additional_lines_y = staffBottom; // - (int) TabWriter.staffGap;
    while ((additional_lines_y - TabWriter.staffGap) >= note_y) {
      canvas.moveTo(x, additional_lines_y);
      canvas.lineTo(x + 7.0f, additional_lines_y);
      additional_lines_y -= TabWriter.staffGap;
    }
    additional_lines_y = staffTop + (int) TabWriter.staffGap;
    while ((additional_lines_y + TabWriter.staffGap) <= note_y) {
      canvas.moveTo(x, additional_lines_y);
      canvas.lineTo(x + 7.0f, additional_lines_y);
      additional_lines_y += TabWriter.staffGap;
    }

Listing 5: drawing leader lines

I'm still not quite done here, though — the sharped notes don't have their sharp markers! Now, in the case of sharped notes, the note is sharped if the fretted position is even when between 1 and 7, odd when between 8 and 12, even when between 13 and 19, odd when between 20 and 24, and so on. I can determine whether a note should be sharped using:

    ((((normalizedFret % 12) + (((normalizedFret % 12) + 6) / 7)) % 2) == 1)
Which, in this case, can be simplified algebraically to:
    if ((((8 * (normalizedFret % 12) + 6) / 7) % 2) == 1)  {
      canvas.showTextAligned(PdfContentByte.ALIGN_LEFT, "#",
        x + 7.0f, note_y, 0);
    }

Here, I just output the octothorpe (hexadecimal 23) rather than the official Unicode sharp marker U+266E.

So here I now have enough to put together a somewhat respectable-looking tablature sheet. I've talked about how to represent and render a single note, but all music (all interesting music anyway) consists of at least a few notes grouped into measures. In addition to this, a guitar allows for as many as six distinct notes to be played simultaneously - that is, it allows for chords to be played. Hence, my internal representation for a piece of guitar music is a triply-nested array of array of arrays of notes: the inner array (which may consist of just a single note) is a chord, the next-level array is a measure — a collection of chords/notes — and finally the outer array is a collection of measures. I suppose I could be more object-orientedly correct here and declare actual types for these concepts, but for now a triply-nested array seems to do fine.

All that's left is to iterate over this triply-nested array structure, invoking renderTab and renderNote once for each note, adjusting the x and y values along the way. I go through and output the tablature first, and then start back at the top and render the notes. This is because of an oddity in the iText interface (and the underlying PDF format) — I can output all of the textual tablature at one time to speed things up, but I have to output each note individually; that's the only way to cause some to appear filled in and some to appear hollow.

Listing 6 puts the whole thing together; the notes themselves are embedded in the code listing here, but it would be a simple matter to read in a textual input file and dynamically create the input array from it.

import java.io.IOException;
import java.io.FileOutputStream;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.CMYKColor;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;

enum GuitarString  {
  E(0,"E"),A(1,"A"),D(2,"D"),G(3,"G"),B(4,"B"),e(5,"e");

  public int offset;
  public String name;

  GuitarString(int offset, String name)  {
    this.offset = offset;
    this.name = name;
  }

  public String toString()  {
    return name;
  }
};

enum Duration  {
  whole(1), half(2), quarter(4), eighth(8), sixteenth(16);

  public int frequency;

  Duration(int frequency)  {
    this.frequency = frequency;
  }
};

class Note  {
  GuitarString str;
  int fret;
  Duration dur;

  public Note(GuitarString str, int fret, Duration dur)  {
    this.str = str;
    this.fret = fret;
    this.dur = dur;
  }

  /**
   * Show this note on an already-rendered staff.  Draw the fret and string positions
   * on the tablature part.
   * @param canvas an open "pdf" canvas
   * @param x the x coordinate, relative to the page, of the bottom part of the
   *   tablature marker.  The note should be rendered immediately above this X coordinate
   *  (and it's the responsibility of the caller to ensure that this values advances
   *  correctly).
   * @param y the y coordinate of the bottom-left part of the tablature marker.
   */
  public void renderTab(PdfContentByte canvas, float x, float y)  {
    float offset = str.offset * TabWriter.tabGap;
    canvas.showTextAligned(PdfContentByte.ALIGN_LEFT, Integer.toString(fret), 
      x, y - 2 + offset, 0);
  }

  /** 
   * Compute the location of the corresponding musical note and draw it above the
   * tablature.
   * 1. Normalize the location of the note as though it were on a very long low-E
   * string.
   * 2. The lowest note that a guitar can play is the low E which is three staff heights
   * below the standard one.
   * If the computed location is above or below the standard treble clef, have to write
   * out extra lines to match down to it.
   * @param x the x coordinate at which the note should be output; the caller is responsible
   *  for advancing this once for each note.
   * @param y the y coordinate of the lowest note that can be output; the E one octave
   *   below the first one on the treble clef.
   */
  public void renderNote(PdfContentByte canvas, float x, float y) 
      throws DocumentException, IOException  {
    // "normalized fret" is where you'd play this note on a theoretically very long
    // low E string.
    int normalizedFret = fret + (str.offset * 5) - ((str.offset > 3) ? 1 : 0);
    // staffBottom points to the lowest note that's still on the staff (for computing
    // whether or not to add lines below the staff)
    int staffBottom = (int) (y + (TabWriter.staffGap * 4));
    int staffTop = (int) (staffBottom + (TabWriter.staffGap * 4));

    // Adjust for the places where I jump 'too far' due to the 'missing' sharps.
    int adjustedFret = normalizedFret +
      ((normalizedFret + 5) / 12) +
      ((normalizedFret + 12) / 12);
    // Adjust the y offset, always up
    // divide by 2.0 to make ceil work correctly
    int halfGap = (int) (TabWriter.staffGap / 2);
    float note_y = y + ((adjustedFret / 2) * halfGap);

    // note_y points to the _bottom_ of the music note to be drawn
    // A music note is just a little wider than it is tall
    canvas.ellipse(x, note_y, x + 7.0f, note_y + 5.0f);
    if (dur.frequency > 1)  {
      if (normalizedFret < 19)  {
        canvas.moveTo(x + 7.0f, note_y + 3);
        canvas.lineTo(x + 7.0f, note_y + 3 + (TabWriter.staffGap * 2.5f));
      } else  {
        canvas.moveTo(x, note_y + 3);
        canvas.lineTo(x, note_y + 3 - (TabWriter.staffGap * 2.5f));
      }
     if (dur.frequency > 4) {
      // one flag
      if (normalizedFret < 19)  {
        canvas.rectangle(x + 7.0f, note_y + 3 + (TabWriter.staffGap * 2.0f), 8, 4);
      } else  {
        canvas.rectangle(x - 8.0f, note_y - (TabWriter.staffGap * 2.0f), 8, 4);
      }
      if (dur.frequency > 8) {
       // two flags
        if (normalizedFret < 19)  {
          canvas.rectangle(x + 7.0f, note_y + 3 + (TabWriter.staffGap * 1.0f), 8, 4);
        } else  {
          canvas.rectangle(x - 8.0f, note_y - (TabWriter.staffGap * 1.0f), 8, 4);
        }
      }
     }
    }

    // Is it a sharp?  If so, add a sharp sign
    if ((((8 * (normalizedFret % 12) + 6) / 7) % 2) == 1)  {
      canvas.showTextAligned(PdfContentByte.ALIGN_LEFT, "#",
        x + 7.0f, note_y, 0);
    }

    // If there were additional staff lines on the top or bottom, add them here
    int additional_lines_y = staffBottom;
    int targetNote = (int) note_y;  // convert to int for comparison below
    while (additional_lines_y > targetNote)  {
      canvas.moveTo(x - 2.0f, additional_lines_y);
      canvas.lineTo(x + 9.0f, additional_lines_y);
      additional_lines_y -= TabWriter.staffGap;
    }
    additional_lines_y = staffTop + (int) TabWriter.staffGap;
    while (additional_lines_y <= targetNote)  {
      canvas.moveTo(x - 2.0f, additional_lines_y);
      canvas.lineTo(x + 9.0f, additional_lines_y);
      additional_lines_y += TabWriter.staffGap;
    }

    if (dur.frequency > 2)  {
      canvas.fillStroke();
    } else  {
      canvas.stroke();  // hollow circles for whole and half notes
    }
  }
}

/**
 * Generate a PDF document with a few rows of blank guitar tablature and fill
 * it in with tablature and musical notes.
 */
public class TabWriter  {
  // Remember that Y starts at the bottom and goes up
  public static float marginTop = 35.0f;
  public static float marginLeft = 15.0f;
  public static float staffGap = 6.0f;    // gap between the lines in the music staff
  public static float separatorGap = 25.0f;  // gap between the music staff and the tab staff
  public static float tabGap = 5.0f;   // gap between the lines in the tab staff
  public static float barGap = 35.0f;  // gap between each complete combined staff
  // barGap has to leave at least 9 * staffGap/2 pixels available, because the high-E
  // string on a 24-fret guitar will appear that high above the staff itself.
  public static float clefWidth = 20.0F;

  public static void main(String[] args) throws IOException, DocumentException  {
    if (args.length < 1)  {
      System.err.println("Usage: TabWriter [output filename]");
      System.exit(0);
    }

    Document document = new Document();
    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(args[0]));
    document.open();
    PdfContentByte canvas = writer.getDirectContent();

    Rectangle pageSize = document.getPageSize();
    canvas.setColorStroke(BaseColor.BLACK);
    canvas.setLineWidth(1.0F);

    float staffWidth = pageSize.getWidth() - (2 * marginLeft);
    float staffHeight = (staffGap * 5) + separatorGap + (tabGap * 5);
    // Assume four measures per tab line, apportion them out evenly
    float measureWidth = (staffWidth - clefWidth) / 4.0F;

    float noteWidth = measureWidth / 8.0F;  // leave room for eight eighth-notes per measure

    // Figure out how many bars will fit comfortably on this page
    int barCount = (int) ((pageSize.getHeight() - (marginTop * 2))  / 
      ((staffGap * 5) + separatorGap + (tabGap * 5) + barGap));

    // Figure out how much "extra space" is at the bottom and distribute it evenly between
    // the staves for aesthetic purposes.
    float remainingSpace = (pageSize.getHeight() - (marginTop * 2)) -
      (barCount * ((staffGap * 5) + separatorGap + (tabGap * 5) + barGap));
    int additionalBarGap = (int) (remainingSpace / barCount);

    float y = pageSize.getHeight() - marginTop;
    float x = marginLeft;

    // The bottom of the tab occurs every staffHeight + barGap pixels.
    for (int i = 0; i < barCount; i++)  {
      // Left bar; connects staff to tab
      canvas.moveTo(x, y);
      canvas.lineTo(x, y - staffHeight);
      // Four measure separators, evenly distributed
      for (int j = 1; j < 4; j++)  {
        canvas.moveTo(x + clefWidth + (measureWidth * j), y);
        canvas.lineTo(x + clefWidth + (measureWidth * j), y - (staffGap * 4));
        canvas.moveTo(x + clefWidth + (measureWidth * j), y - ((staffGap * 5) + separatorGap));
        canvas.lineTo(x + clefWidth + (measureWidth * j), y - staffHeight);
      }
      // Draw the last one exactly on the end
      canvas.moveTo(x + staffWidth, y);
      canvas.lineTo(x + staffWidth, y - (staffGap * 4));
      canvas.moveTo(x + staffWidth, y - ((staffGap * 5) + separatorGap));
      canvas.lineTo(x + staffWidth, y - staffHeight);
      for (int j = 0; j < 5; j++)  {
        canvas.moveTo(x, y);
        canvas.lineTo(x + staffWidth, y);
        y -= staffGap;  // I move y just a hair too far here.  Rather than back up, I just account
                        // for it in the multiplications above
      }
      y -= separatorGap;
      for (int j = 0; j < 6; j++)  {
        canvas.moveTo(x, y);
        canvas.lineTo(x + staffWidth, y);
        y -= tabGap;
      }
      y -= barGap;
      y -= additionalBarGap;
    }

    canvas.stroke();

    // Now start outputting the tablature fingerings
    Note[][][] measures = new Note[][][]  {
      // Opening riff of Sweet Child O' Mine
      new Note[][]  {
        new Note[] {new Note(GuitarString.D, 12, Duration.eighth)},
        new Note[] {new Note(GuitarString.B, 14, Duration.eighth)},
        new Note[] {new Note(GuitarString.B, 12, Duration.eighth)},
        new Note[] {new Note(GuitarString.D, 12, Duration.eighth)},
        new Note[] {new Note(GuitarString.e, 15, Duration.eighth)},
        new Note[] {new Note(GuitarString.G, 13, Duration.eighth)},
        new Note[] {new Note(GuitarString.e, 14, Duration.eighth)},
        new Note[] {new Note(GuitarString.G, 13, Duration.eighth)}
      }
    };

    canvas.beginText();
    BaseFont bf = BaseFont.createFont(BaseFont.COURIER, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
    canvas.setFontAndSize(bf, 8.0F);

    // Move to the bottom of the first staff, left-most position (allowing a little room
    // for the treble clef.
    y = pageSize.getHeight() - marginTop - staffHeight;
    x = marginLeft + clefWidth;

    int measureCount = 0;
    for (Note[][] measure : measures)  {
      for (Note[] chord : measure)  {
        for (Note note : chord)  {
          note.renderTab(canvas, x, y);
          note.renderNote(canvas, x, y + staffHeight - (staffGap * 8));
        }
        x += noteWidth;
      }
      x = marginLeft + clefWidth + (measureWidth * ++measureCount) + 5.0f;
      if (measureCount == 4)  {
        y = y - barGap - staffHeight - tabGap - additionalBarGap;
        x = marginLeft + clefWidth;
        measureCount = 0;
      }
    }
    canvas.endText();

    document.close();
  }
}

Listing 6: The full TabWriter application

The output is illustrated in figure 3, or you can download The full PDF. The clunkiest part is the "flags" on the notes; I just represent these with simple rectangles rather than trying to compute bezier curves and fill those in. Still, the output is quite an improvement over the ASCII-art tab charts you find all over the internet.

Figure 3: Opening few bars of Sweet Child O' Mine

There's a lot missing from this iteration. Probably the most jarring for a professional musician is the absence of key signatures or flatted notes. I also space out the notes horizontally in equal measures, which isn't right — a half note should take up half of the horizontal space. Rest notations (hats and other symbols that indicate that you should not play for a particular duration) are conspicuously absent as well. Of course, all of the decorations like the 4/4 marker, the treble clef symbol, repetition bars, etc. don't appear in this rendition either. A lot of (but not all of) these have Unicode characters, but don't appear in every font on every implementation — I'd be better off in most cases importing graphics rather than relying on font support.

Most of these are just a matter of implementation, but one thing that really makes music notation look professional is the joining together of "related" flags on eighth notes called beams, which would be much more difficult to achieve.

But I must admit that the piece that needs the most work is the input interface! Right now, the only way to generate a new bit of tablature is to update the code; there should be, at a minimum, an input file to convert from. I don't do any error or sanity checking; if you try to stick 100 whole notes on the 57th fret of the high e-string, I'll do my best to render it. But, as difficult as it is for a command line fanatic like myself to admit, this is really the sort of thing that could really benefit from a GUI. That would be a pretty big refactoring, though, because the PDF interface isn't quite the same as, say the Swing or SWT interface; all output should be routed through a common interface so that the same display can be rendered on a print-ready PDF as easily as on a screen.

Add a comment:

Completely off-topic or spam comments will be removed at the discretion of the moderator.

You may preserve formatting (e.g. a code sample) by indenting with four spaces preceding the formatted line(s)

Name: Name is required
Email (will not be displayed publicly):
Comment:
Comment is required
My Book

I'm the author of the book "Implementing SSL/TLS Using Cryptography and PKI". Like the title says, this is a from-the-ground-up examination of the SSL protocol that provides security, integrity and privacy to most application-level internet protocols, most notably HTTP. I include the source code to a complete working SSL implementation, including the most popular cryptographic algorithms (DES, 3DES, RC4, AES, RSA, DSA, Diffie-Hellman, HMAC, MD5, SHA-1, SHA-256, and ECC), and show how they all fit together to provide transport-layer security.

My Picture

Joshua Davies

Past Posts