Let Your Characters Dance and Wiggle

As a human being, I prefer my characters to stand up straight, on a perfect horizontal line. But as a programmer, I like them to dance and wiggle!

And, here is the result: a ready-to-use C++ class to put a string on a curve, circle, sloped line, or any other figure. My class QPathText does it all.

It operates in the GDI+ realm, the graphics extension that was incorporated in Windows XP (and which can be downloaded for earlier versions). QPathText produces a GraphicsPath with curved text in it, ready for display. The text is arranged along another GraphicsPath, which I call the guide path.

Using QPathText

QPathText has a very simple public interface, which can be summarized as follows:

class QPathText
{
public:
   QPathText(const GraphicsPath& guidePath, REAL flatness =
      10.0f * FlatnessDefault);
   ~QPathText();

   void SetFont(const LOGFONT& logfont, bool bFlipY = true);
   int GetPathText(GraphicsPath& path, LPCTSTR str, REAL indent =
      0) const;
   void SetRotation(REAL rot);
   REAL GetRotation() const;
   ...
}

QPathText's constructor takes a reference to the guide path. After construction, the font can be set by using the SetFont() method. It takes a reference to a LOGFONT as its parameter. Only TrueType, OpenType, and PostScript Type 1 fonts will work, but of course nowadays that's almost equivalent to any font. SetFont() has a second parameter, a boolean called bFlipY. This determines how QPathText sees the vertical dimension. If bFlipY is false, it assumes that the vertical axis is going upward, and if it's true, the opposite is assumed.

You'll notice soon enough whether or not bFlipY has the correct value, because if it doesn't, the text characters will appear mirrored and upside down. In default situations—that is, when working in Windows mapping mode MM_TEXT—the y-axis goes down, and bFlipY should have its default value of true.

After the font is set, it's just a matter of calling the member function GetPathText() to output the curved text to a second GraphicsPath. You can draw it to the screen, or do any of the many other things you can do with a GraphicsPath.

Apart from the output GraphicsPath and the text string, GetPathText() has a third parameter, indent. This determines the point along the guide path where the first character of the string will appear. If it's zero (its default value), the text will start right at the beginning of the guide path. The indent is measured in logical co-ordinates, along the guide path.

If the text string happens to be too long to fit along the guide path, it will simply be truncated. You can monitor that by looking at the return value of GetPathText(), which is the number of characters it has successfully processed.

GetPathText() will not empty the GraphicsPath before it adds the curved string to it. This means that a curved string can be added to any other figure.

Rotation

Rotation values of 1.0 (left), 0.5 (center), and 0.0 (right)

The QPathText class has one extra setting: the amount of rotation that will be applied to the characters when they're arranged along the guide path. It can be set with the SetRotation() member function. It's a REAL (float), which can vary between 0 and 1. A value of 1 (or 100%, the default value) means that the characters are rotated as much as needed, and are not deformed. A value of 0 means that the characters are not rotated at all. Instead, they are sheared, or skewed, to follow the path. Other values have an intermediate effect.

Notice that QPathText is designed for western, horizontal left-to-right running text. Other fonts will probably work, but yield unacceptable results.

QPathText is in the header file QPathText.h and the source file QPathText.cpp. The code is not dependent on MFC, ATL, or any other framework. It just uses bare Windows API calls. Consequently, it can be used in pretty much any kind of Windows C++ project. The demo that accompanies this article is a small MFC program.

Inside QPathText

QPathText is derived from another class, QGraphicsText, which is useful on its own and is described in a separate article. It does a lot of the hard work, such as retrieving the glyph data from the depths of Windows.

Although the base class does a lot of the hard work, QPathText is responsible for positioning each character along the guide path. In particular, it calculates the lower right point of the character cell, given the origin.

Finding the correct position for the lower right point of a character cell boils down to determining the intersection point of a circle and a 'flattened' curve.

First the guide path is 'flattened' when it's loaded (in QPathText's constructor), so that you'll only have to consider straight line segments. Then, you'll have to find the point on the guide path, having a distance to the origin, equal to the character cell width. In other words, you're looking for one of the intersection points of the circle centred on the origin and having a radius equal to the cell width, with the flattened guide path. It's high school geometry, involving the solving of a quadratic equation, but don't worry: QPathText takes care of everything.

From the lower left and lower right points, a GDI+ Matrix can be filled in, to transform the obtained glyph data. Of course, you need to do some more math. But, once you've come this far, that's really a piece of cake.

Note: your system must support GDI+, which currently only XP does natively. However, other Windows versions can be upgraded. Also, VC++ 6.0 comes without the GDI+ headers. You may obtain them by downloading the Windows Platform SDK. The GDI+ headers are included with VC++ 7.0 and later.


Downloads

Comments

  • Great

    Posted by s_basawaraj007 on 09/29/2010 03:54am

    awesome..!

    Reply
  • Excellent! But how to calculate the Indent before calling GetPathText?

    Posted by edwinyeah on 02/08/2007 03:54am

    I need to align the text in the center of the path. thanks.

    Reply
  • GNU LGPL

    Posted by Odegaard on 04/18/2006 01:44pm

    Would you consider releasing your library under LGPL instead of GPL? I have a few LGPL libraries that this could work great with, but the GPL license is a somewhat too restrictive with respect to libraries.

    Reply
  • QPathText In C#

    Posted by PhZ.ZA on 02/13/2006 09:23am

    I Converted The Code To Be Compiled In a C# DLL. To DownLoad Go To "WWW.E-Technik.com" to download This As Well As other Brilliant Freeware & Shareware Applications and Libraries. Converted Code is available at : "http://www.e-technik.com/downloads/pathedtext.zip" Regards Andrew (E-Technik Developer)

    Reply
  • It's an excellent work

    Posted by marcelchevalier on 01/30/2006 02:24pm

    I congratulate you for your excellent work, it's wonderfull

    Reply
  • I think are a psycho... bad is soooooo cool!!

    Posted by Pinky98 on 10/24/2005 04:57pm

    This is a fantastic piece of work. Well done.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • 10 Rules that Make or Break Enterprise App Development Projects In today's app-driven world, application development is a top priority. Even so, 68% of enterprise application delivery projects fail. Designing and building applications that pay for themselves and adapt to future needs is incredibly difficult. Executing one successful project is lucky, but making it a repeatable process and strategic advantage? That's where the money is. With help from our most experienced project leads and software engineers, …

  • Between contractors and employees, there is a great deal of risk surrounding the privacy and security of sensitive data in the healthcare industry. When a breach is suspected, the law requires that healthcare organizations quickly launch an investigation to find the source and scope of the issue. These investigations can be quite costly, to say nothing of the penalties for the breach itself and the damage to reputation and brand. This white paper details new affordable software tools that can help healthcare …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds