Click here to Skip to main content
Click here to Skip to main content

Vertical Labels in Web Pages

By , 20 Sep 2004
 

Sample Image - Hello colorful world!

Introduction

A recent project had to display long labels in the header of a table with a lot of columns. While the columns' content kept small, the header was growing huge. The best way to accomplish the task would have been to use 900 rotated texts. However, I did not know any techniques for rotating texts in HTML. So - here is the idea of another WebControl for creating a bitmap on the fly and rendering it in the page to simulate a vertical label.

During developing this project, I've encountered many articles dealing with creating images on the fly. However, lots of small details came in the way and I feel that summarizing them in an article would be beneficial.

Prerequisites

The only choice I had was to - somehow - build small bitmaps and use them in <IMG SRC=...> tags. Then, create a WebControl for simplifying the task of building pages. A WebControl would have an additional benefit: it could bind its Text property so the page would dynamically create labels or localize them.

However, I do not want to store all generated images in files on the hard drive: each file would have a unique name and, beside the problem with generating (long) names, files will soon accumulate in large quantities on the server and somehow somebody should manage or delete them. Fortunately, the IMG SRC tag attribute accepts a URL as the file name, and this URL can be as well the URL of a program/script/page which returns a stream in the same way the bitmap of GIF/JPG file would.

So, the task splits in two smaller tasks:

  1. Create a generator page for the content needed in the SRC attribute,
  2. Wrap all that in a WebControl.

VerticalText page

Let's start coding the VerticalText.aspx page:

namespace WebImageTest {
  /// <summary>
  /// VerticalText is a peculiar page which returns
  /// an image instead of an HTML code.
  /// </summary>
  public class VerticalText : System.Web.UI.Page {

  private void Page_Load(object sender, System.EventArgs e) {
      
//...
      Response.ContentType = "image/png";
      Response.BinaryWrite( ms.ToArray() );
//...
  }
//...
  }
}

This ASPX is peculiar in the sense of its content: instead of returning HTML content (<HEADER>, <BODY> etc.), it returns a picture. This is achieved in the Page_Load method, with the line:

Response.ContentType = "image/png";

ContentType, with the default "text/HTML" can change the response in various ways and produce interesting results. For example, you may start Excel on the client machine (assuming that the client machine has Excel installed.) In the same way, with a content of "image/bmp", you may start directly Paintbrush or with "image/gif" - PhotoPaint (assuming the corresponding file-extension associations).

The above code generates PNG (Portable Network Graphic) content.

We target that, when typing the following address http://localhost/WebImageTest/VerticalText.aspx?Text=Hello%20World&Font=Arial|24|B, the browser would render the following image:

Direct call to VerticalText.aspx page

It is worth mentioning here why I had to choose the PNG format over all other formats available. First of all, with GIF, I would have been able to use the following (shorter/fewer) lines:

      Response.ContentType = "image/gif";
      bm.Save(Response.OutputStream, ImageFormat.Gif);

However, the output generated this way is raster-ed by the usage of a palette of 256 colors. Saving in the OutputStream works as well for the JPEG format, but the JPEG image is a bit dirty due to an uncontrollable compression rate. On the other hand, the PNG format looks well on the screen, but cannot be saved in the OutputStream (albeit there is no hint why the trick with creating a MemoryStream works!)

PNG image magnified 3 times Gif image magnified 3 times Jpg image magnified 3 times

Let's throw some parameters

Of course, we need a parameter for the text of the label. Then it is as well important to be able to specify the font, unless we want to stick with the defaults. Then, we may extend a bit the idea by being able to change the background color and the ink of the text. A lithe padding would be beneficial in certain situations.

Defaults are good: when users forget or don't need to give values, the object should be able to render something instead of giving the x box. Page_Load starts by getting these values from the request URL. By design, the URL should look like this:

VerticalText.aspx?Text=Hello World&Font=
   Arial|24|B&BgColor=DarkGoldenrod&FrColor=Orange&Padding=3

As everybody knows, after the name of the page name, parameters are introduced with ? and are separated with &. To simplify and reduce the length of the line, I have compacted the font parameters using the | separator and ruling an order as: <Font name>|<Font Size>|<Font attributes>. In fact, the code:

    string fontName = "Arial";
    int fontSize = 12;
    FontStyle fontStyle = new FontStyle();

    try {
      string [] fontStr = 
        Request["Font"].Split(new char[] {'|',',',' ','/',':',';'});
      try { if (fontStr[0]!=string.Empty) fontName = fontStr[0]; } catch { }
      try { fontSize = int.Parse(fontStr[1]); } catch { }
      try { 
        if (fontStr[2].IndexOf('B')>=0) fontStyle |= FontStyle.Bold; 
        if (fontStr[2].IndexOf('I')>=0) fontStyle |= FontStyle.Italic; 
        if (fontStr[2].IndexOf('U')>=0) fontStyle |= FontStyle.Underline; 
      } catch {}
    } catch {}
      
    Font font = new Font(fontName, fontSize, fontStyle);

allows a lot more delimiters (including blank) and prepares the defaults. The try...catch blocks are there just to make sure that defaults will stay when the request does not have requested elements.

Do the same for bgColor, frColor and pad. Problems with colors: first of all, I want my users to be able to use named colors as Red, or DarkGoldenrod or ControlDark. In the same time, I want to be able to choose any unnamed color like #804040. Well, there is a glitch that I could not fix: I can not use the # char in the URL. So, I decided to change it in $. The private method StrToColor will do the rest, and I believe is clear enough:

  private Color StrToColor(string c) {
    if (c.Trim().IndexOf("$")==0) 
        return ColorTranslator.FromHtml(c.Replace("$","#"));
    else
        return Color.FromName(c);
  }

Then set some defaults and decode the colors:

    int pad = 0;
    Color bgColor = Color.White;
    Brush br = Brushes.Black;

    try { pad = int.Parse(Request["Padding"]); } catch { }
    try { bgColor = StrToColor(Request["BgColor"]); } catch { }
    try { br = new SolidBrush( StrToColor(Request["FrColor"]) ); } catch { }

From a different angle

There are two ways to measure the size of the bitmap: measure the string rendered horizontally and reverse the width with the height, or use StringFormatFlags.DirectionVertical. (Thanks to Chris Garrett for this trick!):

StringFormat format = new StringFormat(StringFormat.GenericDefault);
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | 
                     StringFormatFlags.DirectionVertical;

SizeF sz = (Graphics.FromImage(new Bitmap(1,1))).MeasureString(Text, 
                                          font, Point.Empty, format);

Bitmap bm = new Bitmap((int)sz.Width+2*pad,(int)sz.Height+2*pad);

Drawing the string this way, however, brings a perceptive problem: the string starts with the first character on the top while the bottom of the text is oriented towards the left. As I have the European school, I know that to be easy to read a vertical text, the string should be oriented the other way. There are pros and cons for that, and I'm going in to details. Firstly, at technical drawing lessons, I've learned that one should take a blueprint with the right hand from the upper-right corner and with the left hand from the lower-right corner. Then drag the paper from vertical to horizontal and the vertical text should become horizontal in the natural way. Americans however, do the other way (like all the other ways to figure how to do a projection in drawing!) and same does the Microsoft flag for rendering vertical texts. Personally, I feel that, in front of a fixed screen, it's easier to turn the head to the left instead of the right to read a vertical text (it's so natural that you do not have even to turn the head). Observe that Excel also adheres to this rule when it rotates texts in cells.

We can also argue about how multiple lines vertical text should be rendered, but for a text box, it makes no difference - can go both ways. However, for my project, I have to put vertical labels in the header of a table and we read table columns (as normal text) from left to right:

Table stuffed with Vertical Labels header

Tip: to make the vertical text even more readable, make it Italic.

So, the bottom line is that we have to rotate the text again:

    System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bm);
    try {
      g.Clear(bgColor);
      g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
      //AntiAliasGridFit; //AntiAlias;

      g.TranslateTransform ((int)sz.Width,(int)sz.Height);
      g.RotateTransform(180.0F);
      g.DrawString(Text, font, br, -1-pad, 1-pad, format);

Now, push the bitmap content in the Response and clean up the resources:

      MemoryStream ms = new MemoryStream();
      bm.Save(ms,ImageFormat.Png);
      Response.ContentType = "image/png";
      Response.BinaryWrite( ms.ToArray() );
      ms.Close();
    } finally {
      g.Dispose();
      bm.Dispose();
      font.Dispose();
    }

Peculiar use

To refer the VerticalText.aspx page directly from the address bar of the browser makes not much sense - it will render just a label per screen.

We target, however, to use this reference in the SRC= attribute of images (<Img tag) or image buttons. For example, using in a form the following code:

<asp:ImageButton id="ImageButton1" runat="server" 
  ImageUrl="VerticalText.aspx?Text= Login &Font=|16|B&BgColor=Silver&FrColor=Window"
  BorderStyle="Outset" BorderWidth="1px" AlternateText="Login">
</asp:ImageButton>

we may get ourselves a beautiful vertical button:

Beautiful vertical button

Now, pack it up!

The second task on our list was to pack all that in an easy to use WebControl - the VerticalLabel. The control will have to manage with its properties the text of the label, the font, and the colors. It will be also responsible for rendering the HTML code that uses the above VerticalText.aspx page. It would be nice to have the control on the ToolBox with a nice glyph to represent it there. (I have already discussed what the challenges for creating this small bitmap are and how to put it on the ToolBox, in my previous article Numeric Text Box.)

So, inherit the VerticalLabel control directly from the WebControl and add a reference to the INamingContainer interface, so the control will get a unique ID. Accept as it is the auto generated Text property with its Bindable(true) attribute. Observe also that the control has by default a Font and a BackColor. What we miss is the Padding property that we add ourselves.

Next, we just have to generate the HTML code in the Render method:

    protected override void Render(HtmlTextWriter output) {
      StringBuilder s = new StringBuilder("<img ID="); s.Append(this.ID);
      s.Append(" src=\"VerticalText.aspx?Text="); s.Append(Text);
      s.Append("&Font="); s.Append(Font.Name); s.Append("|");

Using a StringBuilder seems to be a good idea. (Although we could write directly in the output. However, this way may help when debugging.) Keep adding items separately instead of concatenating them with the + string operator. Let's have the Font parameter added anyway, whether its name is empty or not - the VerticalText.aspx page will have a default for that. In the same way, add the size and the font attributes by the rules VerticalText.aspx page accepts:

      if (Font.Size.Unit.Value>0) 
        s.Append(Font.Size.Unit.Value.ToString());
      if (Font.Bold || Font.Italic || Font.Underline) {
        s.Append("|");
        if (Font.Bold     ) s.Append("B");
        if (Font.Italic   ) s.Append("I");
        if (Font.Underline) s.Append("U");
      }

Don't add the BackColor and ForeColor when they had no value, but use a WebColorConverter if it was a known color, or the ColorTranslator to convert ToHtml otherwise. Remember, we were not able to use the # char in the URL, so replace it with $.

      if (!BackColor.IsEmpty) {
        s.Append("&BgColor=");
        s.Append(((BackColor.IsKnownColor)
          ?(new WebColorConverter()).ConvertToString(BackColor)
          :ColorTranslator.ToHtml(BackColor).Replace("#","$")));
      }
      if (!ForeColor.IsEmpty) {
        s.Append("&FrColor=");
        s.Append(((ForeColor.IsKnownColor)
          ?(new WebColorConverter()).ConvertToString(ForeColor)
          :ColorTranslator.ToHtml(ForeColor).Replace("#","$")));
      }
      if (Padding>0) {
        s.Append("&Padding=");
        s.Append(Padding.ToString());
      }
      s.Append("\"");

Just to have a horizontal text if something goes wrong in the VerticalText.aspx page, add the ALT= attribute to the HTML component. This might be a little odd when everything goes well, since the Alt text becomes for the label a hint with exactly the same text; so, you may decide to keep or discard the next line:

      s.Append(" Alt=\""); s.Append(Text); s.Append("\""); // !

Close the tag and write all that to the output. When debugging, you may place a breakpoint on the output statement or anywhere above to see how the string accumulates.

      s.Append(">");
      output.Write(s.ToString());
    }

Wish List

I wish I was able to include the VerticalText.aspx page in the assembly of the VerticalLabel control. It seems that this is not possible - the page should be part of the final assembly or be a separate one. (Then the above code should include the full path to that assembly, the control should include a property to point to that assembly URL, and the user would have to type that URL seed for each vertical label in the project; considering all that, it seems that the price to copy the VerticalText.aspx page in the final assembly is not too high.)

When I've finished writing this project, an article from asp.netPRO magazine (September 2004) - "Custom HTTP Handlers" by Dino Exposito - was suggesting that it would have been possible to use an IHttpHandler interfaced class to achieve the same effect as with the VerticalText.aspx page. I might go in that soon, but again - the price to pay for reconfiguring the IIS and having the handler active on any other project from the server seems to be as well too high at this point. Maybe with some added functionality as variable angles, graphic effects, and retrieving background/watermark images from databases or other sources...

I wish also being able to include characters like & and # in the caption of the label or in the specification of unknown colors.

The technique described here does not support transparent bitmaps. There is an excellent article - "Transparent GIFs with GDI+ / System.Drawing" about re-coloring GIFs and making them transparent, but the code is a bit too stuffy, and I'm not going yet to include it in here; in fact, my project works perfectly without...

Maybe, I'll get some good suggestions from you...

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Horia Tudosie
Web Developer
Canada Canada
Member
Horia Tudosie is Master of Sciences from the Polytechnic University of Bucharest - Computer Sciences.
Horia came in Canada in 1994 and works in US since 2003.
He was working with various peculiar languages as Delphi and Forth, but mainly with C, C++ and C#.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1memberMember 894945031 Aug '12 - 1:38 
vbnm,
GeneralNice articlememberSrinath G Nath22 Sep '08 - 21:20 
Good Show.,.,   Very nice article.,.,
Questionms?memberRammoneX11 Jul '08 - 4:03 
Hi?   You wrote Response.BinaryWrite(ms.ToArray())   what does "ms" stand for?
AnswerRe: ms?memberHoria Tudosie16 Sep '09 - 6:20 
using(MemoryStream ms = new MemoryStream()) { bm.Save(ms,ImageFormat.Png); Response.ContentType = "image/png"; Response.BinaryWrite( ms.ToArray() ); }   ms is the memory string used to serialize the content of the bitmap bm where you have rendered your image.  ...
GeneralVerticalText.aspx.csmemberwcpeabody29 Sep '04 - 5:53 
I am not able to get this sample to work. Can you post the VerticalText.aspx.cs? There is something I am missing or not seeing that is keeping me from figuring this out.   Thanks, Bill
GeneralRe: VerticalText.aspx.csmemberHoria Tudosie30 Sep '04 - 4:08 
VerticalText is already part of the archive. However, as I've explained in the article, it cannot be part of the VerticalLabel project, but from the final project. So, what you have to do is to move it from VerticalLabel in your project and change the namespace from "WebImageTest" to the...
GeneralRe: VerticalText.aspx.csmemberwcpeabody30 Sep '04 - 4:17 
I do not know how I missed it??? I look again and there was the file. Thanks for the reply and sorry for not seeing it the first time.   Thanks again, Bill
GeneralRe: VerticalText.aspx.csmembereilering5 Nov '05 - 19:44 
Hai, I'm Frans from Holland and trying to create vertical labels. I succeeded but not without some study. I was new with webcontrols. My results:   First I studied http://www.15seconds.com/issue/020430.htm[^], a great article about webcontrols.   Then I created my verticallabel.dll...
GeneralEuropean versus American orientationmemberDennis Dykstra21 Sep '04 - 15:42 
This is an excellent article on a very useful topic. However, I'm not sure I understood your comparison of the European way of orienting vertical text versus the American way of doing it. All of your examples are consistent with the normal way it is done in the US. I think it's the same in...
GeneralRe: European versus American orientationmemberHoria Tudosie23 Sep '04 - 5:12 
I'm also not sure what you mean by "words point upward". Here is a picture of some American books in my bookcase. You can see that American books have words on spines starting on top and going down. This is consistent with what I’ve said: It makes turn the head toward the right for reading and...
GeneralRe: European versus American orientationmemberDennis Dykstra23 Sep '04 - 12:58 
You're absolutely right--American spines have words pointing the wrong way: Downward, NOT upward! Had I twisted my head from looking straight ahead at my CRT, I'd have noticed the bookshelf on my left with all of the spines pointing downward. Very disturbing!   What I should have said...
GeneralRe: European versus American orientationmemberSpiff Dog23 Sep '04 - 14:54 
Howdy.   You are quite right. The "normal" orientation for vertical aligned text is 90° counter-clockwise.   The reason the spine text on a book is oriented differantly is for convinience. The text on the spine can be read easily while it is lying on it's back.   If you...
GeneralRe: European versus American orientationmemberPhilippe Lhoste4 Oct '04 - 22:40 
I don't understand why this orientation is "wrong"...   Indeed, for vertical labels of tables, we are used to see text going upward.   But for books, CDs, videos, I believe that putting the text so it can be read with the object lying on the back, which is a "natural" way of...
AnswerRe: VerticalText.aspx ?memberhoriatu20 Sep '04 - 8:18 
Hi Mike,   Sorry - in the last moment I've decided not to download any test project, and - as I've stated in the article - VerticalText.aspx should be part of the final project. I have just fixed that: please download again the source code and care to move that page in your test project....
AnswerRe: VerticalText.aspx ?memberPapichulo.NET3 Aug '06 - 9:15 
Why you all don't use style pages like this one:   <style> .vertical { writing-mode: tb-rl; filter: flipH flipV; }   </style>   this stile make the vertical   eg. This is an html <html> <head> ...
AnswerRe: VerticalText.aspx ?memberHoria Tudosie27 Sep '06 - 10:34 
That's an excelent solution for vertical labels.   However, the artice is more than that: lately I've been using the framework described there to develop on the fly other kind of graphics: charts or animated gifs. I hope that others will find this project useful to understand how to...
GeneralRe: VerticalText.aspx ?memberMattsterP12 Apr '07 - 15:02 
That works fine in IE, but "writing-mode" is not supported in other browsers, which means an image generation method seems to be the best Cross-Browser solution to the vertical text problem.   -Matt
QuestionVerticalText.aspx ?memberMike Ellison20 Sep '04 - 7:47 
Hi there. Interesting article. I didn't find the VerticalText.aspx page with the article download - is that an oversight?   Thanks
GeneralortLimited CSS Supportsussronnyek20 Sep '04 - 5:15 
There is support for this in recent versions of css, although I dont really know how well its supported.   I wrote a "timecard" app that needed to show dates in a verticle means like that and it worked perfect under IE, cant say if it worked under anything else.
GeneralRe: ortLimited CSS SupportmemberCypher1 Oct '04 - 2:17 
Indeed.   For text-only vertical labels CSS does the trick... I implemented this on my website in the making. Of course, you have to provide a work-around (which I did) for non IE browser (5% of them?).   Below CSS code and implementation. No need to go through heavy-load...
GeneralRe: ortLimited CSS SupportmemberHoria Tudosie1 Oct '04 - 3:44 
Excelent. How did you provide a work-around for non IE browsers?   Horia Tudosie
GeneralRe: ortLimited CSS SupportmemberHoria Tudosie23 May '08 - 4:04 
Vertical Labels was a pretext to show how to work with auto-generated images without saving them on the disk before exposing them in a web page. However, your point is good to remember! Thanks.   Horia Tudosie
GeneralRe: ortLimited CSS SupportmemberAlex Gamperl12 Oct '10 - 23:45 
writing-mode: tb-rl; is not supported any more with IE7 it is now -ms-writing-mode : tb-rl; and it does not work properly. the hight of the columns ist much to big for the content :(
GeneralRe: ortLimited CSS SupportmemberHoria Tudosie13 Oct '10 - 2:08 
Can you add a picture to illustrate your a assertion? Horia Tudosie
GeneralRe: ortLimited CSS SupportmemberHoria Tudosie27 Nov '12 - 5:05 
By the time I've written the article, there was no support in CSS for vertical text orientation (at least not in all of them.) However the article remains a good reference for rendering all kind of other images on the fly. Horia Tudosie

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 20 Sep 2004
Article Copyright 2004 by Horia Tudosie
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid