Click here to Skip to main content
15,671,149 members
Articles / Multimedia / GDI
Posted 24 Feb 2014


36 bookmarked

Matrix Printer Class

Rate me:
Please Sign up or sign in to vote.
4.82/5 (13 votes)
24 Feb 2014CPOL4 min read
A C++ class that makes it easy to print text in clearly defined rows and columns

Sample printout using Pristina font.

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.


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


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.



Pointer to a string containing the name of the document file.


Pointer to a DEVNAMES structure used to create the printer device context.


Pointer to an optional DEVMODE structure used to initialize the printer device context.


Pointer to a string containing the name of the printer device, as shown by the Print Manager.


Pointer to a string containing the name of a print provider, defaults to "WINSPOOL".


Handle to a printer device context.


Reference to a DOCINFO structure containing the name of the document file and the output file.



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.



The number of rows requested to be in the matrix.


The number of columns requested to be in the matrix.


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.



Pointer to a string that contains the text to be placed.


The row or line on which the text will be placed.


The column where the left most character in the text will be placed.


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.



The row that contains the glyph rectangle.


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[])
    // Setup and call a print dialog
    PRINTDLG pd = {0};
    pd.lStructSize = sizeof(PRINTDLG);
    pd.Flags |= PD_RETURNDC;


    // Wrap the device context returned from the printer
    // dialog into a CMatrixPrinter class object.
    CMatrixPrinter mp(_T("Matrix Printer Test"), pd.hDC);

    // Setup the matrix using a variable width font
    mp.SetRows(60, _T("Pristina"));

    // Draw the demo grid lines to highlight our matrix
    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.right, rc.bottom);
    SelectObject(mp, OldPen);

    // First place the normal text on row 1 to show what
    // the font looks like normally when drawn normally.
    RECT rc = mp.GetGlyphRect(1, 1);
    TextOut(mp, rc.left,, Text, _tcslen(Text));

    // Then place the same text in our matrix on row 2
    // to show what it looks like now.
    mp.PlaceTextL(Text, 2, 1);

    // Now put some numbers in a column, right justified on column 20
    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;


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

Written By
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

Questionvery nice Pin
BillW3314-Mar-14 7:55
professionalBillW3314-Mar-14 7:55 
AnswerRe: very nice Pin
PJ Arends14-Mar-14 13:33
professionalPJ Arends14-Mar-14 13:33 
GeneralMy vote of 5 Pin
Mike Gaskey1-Mar-14 4:17
Mike Gaskey1-Mar-14 4:17 
GeneralRe: My vote of 5 Pin
PJ Arends1-Mar-14 5:31
professionalPJ Arends1-Mar-14 5:31 
QuestionGet's my 5 Pin
David MacDermot25-Feb-14 14:24
David MacDermot25-Feb-14 14:24 
AnswerRe: Get's my 5 Pin
PJ Arends25-Feb-14 16:40
professionalPJ Arends25-Feb-14 16:40 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.