Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / GDI

Matrix Printer Class

4.82/5 (13 votes)
24 Feb 2014CPOL4 min read 31.8K   1.4K  
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.

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

C++
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

C++
~CMatrixPrinter(void)

Calls the EndPage() and EndDoc() APIs. Also calls DeleteDC() if the device context was created in the constructor.

Matrix Setup Functions

C++
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

C++
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

C++
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

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

C++
int GetPageNumber(void)

Retrieves the current page number.

Return Value

The current page number.

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

C++
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

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

C++
int _tmain(int argc, _TCHAR* argv[])
{
    // Setup and call a print dialog
    PRINTDLG pd = {0};
    pd.lStructSize = sizeof(PRINTDLG);
    pd.Flags |= PD_RETURNDC;

    PrintDlg(&pd);

    // 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.top, 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, rc.top, 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;
}

License

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