Sample printout using Pristina font. Grid lines are for demonstration only. The first line of text is how the font normally looks. The rest is the font set out using the CMatrixPrinter class.
Introduction
I have a bunch of data that is best displayed in neatly arranged rows and columns that are all right justified so that the decimal points all line up in a single column. The GDI API does not have built in functionality to do right justified text although it can be faked by making judicious use of leading whitespace. But that only works if you use a fixed width font. The moment you use a variable width font, all your carefully laid out formatting goes to pot.
The class presented in this article, CMatrixPrinter
, is designed to solve this problem. It is a wrapper class that wraps an HDC
printer device context handle. It has member functions to setup the printing matrix. It also has functions to output the text to specific rows and columns, either left justified or right justified. It also does not matter what font you decide to use as almost all fonts will work just fine.
Using the Code
Public Member Functions
Constructors
CMatrixPrinter(const TCHAR * DocName, LPDEVNAMES pDevNames, LPDEVMODE pDevMode = NULL)
CMatrixPrinter(const TCHAR * DocName, const TCHAR * Device,
const TCHAR * Driver = _T("WINSPOOL"), LPDEVMODE pDevMode = NULL)
CMatrixPrinter(const TCHAR * DocName, HDC hDC)
CMatrixPrinter(const DOCINFO & DocInfo, HDC hDC)
There are four constructors in the CMatrixPrinter
class. They either create the printer device context or wrap the supplied device context. They then call the StartDoc()
and StartPage()
APIs. They also set the text background mode to TRANSPARENT
.
Parameters
DocName
Pointer to a string
containing the name of the document file.
pDevNames
Pointer to a DEVNAMES
structure used to create the printer device context.
pDevMode
Pointer to an optional DEVMODE
structure used to initialize the printer device context.
Device
Pointer to a string
containing the name of the printer device, as shown by the Print Manager.
Driver
Pointer to a string
containing the name of a print provider, defaults to "WINSPOOL
".
hDC
Handle to a printer device context.
DocInfo
Reference to a DOCINFO
structure containing the name of the document file and the output file.
Destructor
~CMatrixPrinter(void)
Calls the EndPage()
and EndDoc()
APIs. Also calls DeleteDC()
if the device context was created in the constructor.
Matrix Setup Functions
int SetRows(int Rows, const TCHAR * FontFaceName = NULL)
int SetColumns(int Columns, const TCHAR * FontFaceName = NULL)
These two functions setup the matrix. They attempt to set the specified number of rows or columns but because of how the fonts are created it may not be exact. SetRows
calculates the number of columns needed and SetColumns
calculates the number of rows. Use CMatrixPrinter::GetMatrixSize()
to get the exact number of rows and columns.
Parameters
Rows
The number of rows requested to be in the matrix.
Columns
The number of columns requested to be in the matrix.
FontFaceName
Pointer to a string
that specifies the typeface name of the font. If this is NULL
or an empty string
, CMatrixPrinter
will use the first monospaced font that it finds.
Return Values
If the functions succeed, the return value is greater than zero.
If the functions fail, the return value is zero.
Text Placement Functions
int PlaceTextL(const TCHAR * Text, int Row, int LeftColumn)
int PlaceTextR(const TCHAR * Text, int Row, int RightColumn)
Use these functions to place the text on the matrix. The text will be either left or right justified on the specified column.
Parameters
Text
Pointer to a string
that contains the text to be placed.
Row
The row or line on which the text will be placed.
LeftColumn
The column where the left most character in the text will be placed.
RightColumn
The column where the right most character in the text will be placed.
Return Values
The functions return the number of characters actually printed. This value can be less then the length of the text if either end of the text ends up outside the matrix.
Paging Function
int NextPage(void)
Calls EndPage()
and StartPage()
APIs in order to advance the printing to the next page.
Return Value
Returns the new page number.
Property Functions
int GetPrintJobID(void)
Retrieves the print job identifier for the document. This is the value returned by the StartDoc()
API.
Return Value
The print job identifier.
int GetPageNumber(void)
Retrieves the current page number.
Return Value
The current page number.
SIZE GetMatrixSize(void)
Gets the dimensions of the print matrix. The cx
value is the columns across the page, and the cy
value is the rows down the page.
Return Value
A SIZE
structure containing the size of the matrix.
RECT GetGlyphRect(int Row, int Column)
Gets the location and size, in pixels, of a glyph rectangle within the matrix.
Parameters
Row
The row that contains the glyph rectangle.
Column
The column that contains the glyph rectangle.
Type Casting Operator
operator HDC(void)
Use this operator to retrieve the wrapped printer device context handle.
Return Value
The handle of the printer device context.
Sample Code
This sample code is the code that was used to generate the sample picture at the top of this article.
int _tmain(int argc, _TCHAR* argv[])
{
PRINTDLG pd = {0};
pd.lStructSize = sizeof(PRINTDLG);
pd.Flags |= PD_RETURNDC;
PrintDlg(&pd);
CMatrixPrinter mp(_T("Matrix Printer Test"), pd.hDC);
mp.SetRows(60, _T("Pristina"));
HPEN Pen = CreatePen(PS_DOT, 1, RGB(128, 128, 128));
HPEN OldPen = (HPEN)SelectObject(mp, Pen);
for (int x = 0; x < mp.GetMatrixSize().cx; ++x)
{
for (int y = 0; y < mp.GetMatrixSize().cy; ++y)
{
RECT rc = mp.GetGlyphRect(y, x);
Rectangle(mp, rc.left, rc.top, rc.right, rc.bottom);
}
}
SelectObject(mp, OldPen);
RECT rc = mp.GetGlyphRect(1, 1);
TextOut(mp, rc.left, rc.top, Text, _tcslen(Text));
mp.PlaceTextL(Text, 2, 1);
for (int i = 0; i < _countof(Numbers); ++i)
{
TextColour TC(mp, Numbers[i] < 0.0 ? RGB(255, 0, 0) : RGB(0, 0, 255));
mp.PlaceTextR(pja::CBuilder<>(_T("${0}"), Fixed(Numbers[i], 2)).c_str(), i + 4, 20);
}
return 0;
}
Father of two, brother of two, child of two.
Spouse to one, uncle to many, friend to lots.
Farmer, carpenter, mechanic, electrician, but definitely not a plumber.
Likes walks with the wife, board games, card games, travel, and camping in the summer.
High school graduate, college drop-out.
Hobby programmer who knows C++ with MFC and the STL.
Has dabbled with BASIC, Pascal, Fortran, COBOL, C#, SQL, ASM, and HTML.
Realized long ago that programming is fun when there is nobody pressuring you with schedules and timelines.