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
   QPathText(const GraphicsPath& guidePath, REAL flatness =
      10.0f * FlatnessDefault);

   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 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.

More by Author

Must Read