##
Introduction

The
function provided here can be used to**
draw a text string**
with an oblique or **
slant angle**. Such
**text**
outputs are useful in isometric or
perspective 3D views to make the
**text strings**
look like in their 3D space. An example is
shown in the following picture:

##
Background

Windows **GDI**
function ```
TextOut()
```

does not allow a **
text slant angle**. To
**draw**
such **slanted
strings**, we need to set a
transformation using ```
```**SetWorldTransform**()

.
Windows **drawing**
function will then take care of the shearing
and rotation of the output. This procedure
is incorporated into a new function similar
to Windows ```
TextOut()
```

function:

void ObliqueTextOut( CDC *dc, int oblique, int x,int y,const CString &Text )

This
function has the same arguments as Windows
```
TextOut()
```

function with an additional argument,
`oblique`

,
to specify the** text
slant angle**. The function can be
placed where ```
TextOut()
```

is normally used.

## Using
the code

Insert
the function **source
code** into your
**source code**
file. Call the function at places where you
would normally call Windows
```
TextOut()
```

function. Remember to select the font, set
the text background mode, color and
background color etc, as you would normally
do before calling ```
TextOut()
```

.

**Angle**
```
oblique
```

is positive if the**
text slants** forward(to the right)
and negative if it **
slants** backwards(to the left).
The ```
oblique
```

angle,
`s`

,
in the figure below is positive. The angle
is in 1/10th degrees. Therefore, if the**
text slants **forward 15 degrees,
oblique=150.

##
Points of Interest

The
key to the question is to set up the
transformation in DC. Function
```
```**SetWorldTransform**()

needs an ```
XFORM
```

structure for the transformation. Therefore,
we need to prepare the ```
XFORM
```

structure before calling ```
```**SetWorldTransform**(
)

.
`XFORM`

has 6 member data. They are ```
eM11
```

,
`eM21`

,
`eM12`

,
`eM22`

,
`eDx`

,
`eDx`

.
They are defined as:

X = eM11 * x + eM21 * y + eDx
Y = eM12 * x + 2M22 * y + eDy

where
```
(x,y)
```

are the
World coordinates and ```
(X,Y)
```

are the Paper space coordinates.

In the
figure below, ```
x,y
```

are the World space axes. The string will
always be drawing at ```
(0,0)
```

and horizontally in the world space.
```
xs,ys
```

are the
Sheared space axes. The transformation from
World to the Sheared space is:

xs = x - y * tan(s)
ys = y

where
```
s
```

is the **slant**
or oblique **angle**.

The
Paper space is noted as
```
X,Y
```

.
From the Sheared space to Paper space, the
transformation is a
**rotation**(angle ```
r
```

)
and translation`(Xo,Yo)`

.

X = Xo + xs * cos(r) + ys * sin(r)
Y = Yo + ys * cos(r) - xs * sin(r)

Where
```
(Xo,Yo)
```

are simply the text insertion point in Paper
space. Substitute ```
(xs,ys)
```

into the above, we get:

X = cos(r) * x + (sin(r)-tan(s)*cos(r)) * y + Xo
Y = -sin(r) * x + (cos(r)+tan(s)*sin(r)) * y + Yo

Compare this to the
```
XFORM
```

structure, it is obvious that:

eM11 = cos(r)
eM21 = sin(r) - tan(s) * cos(r)
eM12 = -sin(r)
eM22 = cos(r) + tan(s) * sin(r)
eDx = Xo
eDy = Yo

The
above is translated into function code(```
dc
```

is the
input device context):

XFORM xForm;
xForm.eDx = (float) x;
xForm.eDy = (float) y;
xForm.eM11 = (float) cos(txtRotate);
xForm.eM21 = (float) (sin(txtRotate) - tan(txtOblique)*cos(txtRotate));
xForm.eM12 = (float) -sin(txtRotate);
xForm.eM22 = (float) (cos(txtRotate) + tan(txtOblique)*sin(txtRotate));
SetGraphicsMode( dc->m_hDC, GM_ADVANCED );
SetWorldTransform( dc->m_hDC, &xForm );

The
call to ```
SetGraphicsMode()
```

is
needed. Otherwise, function ```
SetWorldTranform()
```

will
have no effect. Since now we are drawing in
World space, we need to adjust the font's
rotation(`lfEscapement`

)
to be horizontal and the character
orientation(`lfOrintation`

)
to be from the World X-axis.

LOGFONT lgf;
dc->GetCurrentFont()->GetLogFont( &lgf );
...
lgf.lfOrientation -= lgf.lfEscapement;
lgf.lfEscapement = 0;
CFont horFont;
horFont.CreateFontIndirect( &lgf );
CFont *OldFont = dc->SelectObject( &horFont );

Now,
we can call:

dc->TextOut( 0,0, Text );

The
work is done. But before returning, we need
to restore the graphics mode and font:

ModifyWorldTransform( dc->m_hDC, &xForm, MWT_IDENTITY );
SetGraphicsMode( dc->m_hDC, GM_COMPATIBLE );
dc->SelectObject( OldFont );