Lomse library. API documentation  0.30.0
Printing documents overview

The print API

Lomse is platform independent code and knows nothing about how to print in the operating system used by your application. Therefore, it is your application responsibility to implement printing. Lomse just offers some supporting methods so that implementing printing does not require much work. The default solution offered by Lomse for implementing printing in your application is based on printing bitmaps. Nevertheless, any user application can implement its own drawer classes and do all drawing natively without having to use bitmaps and the BitmapDrawer class implemented by Lomse.

The Lomse print API is four methods, invoked in sequence:

  1. First, determine how many pages to print. To know how many pages the document has, just call Interactor::get_num_pages().
  2. In order to not interfere with screen display, a different rendering buffer and resolution are used by Lomse for printing. Therefore, you will have to:
    • Determine the current settings for printer resolution.
    • Determine the paper size, in pixels, based on printer resolution and the paper size that is going to be used, and inform Lomse by invoking Interactor::set_print_paper_size(). Lomse will automatically scale the score pages so that the fit on the specified size.
    • Determine the bitmap size to used (see Determining print buffer size), allocate memory for this buffer and inform Lomse by invoking Interactor::set_printing_buffer().
  3. Now, invoke Interactor::print_page() to request Lomse to render a page in the provided buffer.
  4. Finally, print the bitmap using the facilities of your operating system.

Normally, your application will have to do the last two steps as many times as pages to print.

Example (using wxWidgets, adapt for your framework/OS):

void DocumentWindow::print_page(PrinterDC* pDC, int page, int paperWidthPixels,
int paperHeightPixels)
{
if (!m_pPresenter)
return;
if (SpInteractor spInteractor = m_pPresenter->get_interactor(0).lock())
{
pDC->SetBackground(*WHITE_BRUSH);
pDC->Clear();
//inform Lomse about printer resolution
spInteractor->set_print_page_size(paperWidthPixels, paperHeightPixels);
//allocate print buffer
Bitmap img(paperWidthPixels, paperHeightPixels);
unsigned char* pdata = img.GetBuffer(); //ptr to the real bytes buffer
spInteractor->set_print_buffer(pdata, paperWidthPixels, paperHeightPixels);
//render page on bitmap
spInteractor->print_page(page-1);
//and send it to the printer
pDC->DrawBitmap(img, 0, 0);
}
}

Determining print buffer size

The necesary bitmap size depends on the desired printing resolution and paper size. Take, for example, a sheet of DIN A4 paper; it measures 210mm X 297mm. For printing on it, the necessary bitmap size will depend on the required resolution, as given by this formula:

pixels = size(mm) * resolution (dpi) / 25,4 

For instance, here you have the required bitmap sizes for some printing resolutions:

Printing resolution Bitmap size for A4 paper
72 dpi 595 x 842 pixels
150 dpi 1240 x 1754 pixels
300 dpi 2480 x 3508 pixels
600 dpi 4960 x 7016 pixels

A list of paper sizes can be found here.

Probably, when a print operation is requested, your operating system has methods for determining the required buffer size and printer resolution, so your application will not have to do the computations.

Save memory by tiling

Using large size papers or printing at very high resolutions can require huge bitmaps. A technique for avoiding this is to split the page in tiles and print tile by tile. To avoid artifacts in the borders of each tile (due to anti-aliasing) it is recommended to add a border to each tile and discard it once Lomse has rendered on the tile. Here you have an example using the wxWidgets framework:

void DocumentWindow::print_page(PrinterDC* pDC, int page, int paperWidthPixels,
int paperHeightPixels)
{
if (!m_pPresenter)
return;
if (SpInteractor spInteractor = m_pPresenter->get_interactor(0).lock())
{
pDC->SetBackground(*WHITE_BRUSH);
pDC->Clear();
//inform Lomse about printer resolution
spInteractor->set_print_page_size(paperWidthPixels, paperHeightPixels);
//determine tile size (pixels)
int width = min(1024, paperWidthPixels);
int height = min(1024, paperHeightPixels);
int border = 8;
if (width < 1024 && height < 1024)
border = 0;
// B = border, w = width
//From paper viewpoint, for copying a tile into paper, copy origin is
//at (B, B) and copy size is (w-2B, h-2B). Initial paper org is at (0,0).
//From render viewpoint, initial viewport origin is at (B, B) and tiles
//size (for advancing viewport origin) is also (w-2B, h-2B).
VPoint viewport(0,0);
VPoint paperPos(0,0);
int tileW = width - 2 * border;
int tileH = height - 2 * border;
//determine how many tiles to print
int rows = int( ceil(float(paperHeightPixels) / float(tileH)) );
int cols = int( ceil(float(paperWidthPixels) / float(tileW)) );
//determine last row and last column tile sizes
int lastW = paperWidthPixels - tileW * (cols - 1);
int lastH = paperHeightPixels - tileH * (rows - 1);
//allocate print buffer
Bitmap img(width, height);
unsigned char* pdata = img.GetBuffer(); //ptr to the real bytes buffer
spInteractor->set_print_buffer(pdata, width, height);
//loop to print tiles.
MemoryDC memoryDC;
for (int iRow=0; iRow < rows; ++iRow)
{
for (int iCol=0; iCol < cols; ++iCol)
{
spInteractor->print_page(page-1, viewport);
//print this tile
int tileWidth = (iCol == cols-1 ? lastW : tileW);
int tileHeight = (iRow == rows-1 ? lastH : tileH);
if (border > 0)
{
memoryDC.SelectObjectAsSource(img);
pDC->Blit(paperPos.x, paperPos.y, tileWidth, tileHeight,
&memoryDC, border, border);
memoryDC.ReleaseSelectedObject();
}
else
pDC->DrawBitmap(bitmap, paperPos.x, paperPos.y);
//advance origin by tile size
viewport.x += tileW;
paperPos.x += tileW;
}
//start new row
viewport.x = 0;
viewport.y += tileH;
paperPos.x = 0;
paperPos.y += tileH;
}
}
}

Other ways of printing

If you do not like the idea of using bitmaps for printing it is feasible to write a Driver class to perform printing using methods specific to your application and operating system.