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:
- First, determine how many pages to print. To know how many pages the document has, just call Interactor::get_num_pages().
- 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().
- Now, invoke Interactor::print_page() to request Lomse to render a page in the provided buffer.
- 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();
spInteractor->set_print_page_size(paperWidthPixels, paperHeightPixels);
Bitmap img(paperWidthPixels, paperHeightPixels);
unsigned char* pdata = img.GetBuffer();
spInteractor->set_print_buffer(pdata, paperWidthPixels, paperHeightPixels);
spInteractor->print_page(page-1);
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();
spInteractor->set_print_page_size(paperWidthPixels, paperHeightPixels);
int width = min(1024, paperWidthPixels);
int height = min(1024, paperHeightPixels);
int border = 8;
if (width < 1024 && height < 1024)
border = 0;
VPoint viewport(0,0);
VPoint paperPos(0,0);
int tileW = width - 2 * border;
int tileH = height - 2 * border;
int rows = int( ceil(float(paperHeightPixels) / float(tileH)) );
int cols = int( ceil(float(paperWidthPixels) / float(tileW)) );
int lastW = paperWidthPixels - tileW * (cols - 1);
int lastH = paperHeightPixels - tileH * (rows - 1);
Bitmap img(width, height);
unsigned char* pdata = img.GetBuffer();
spInteractor->set_print_buffer(pdata, width, height);
MemoryDC memoryDC;
for (int iRow=0; iRow < rows; ++iRow)
{
for (int iCol=0; iCol < cols; ++iCol)
{
spInteractor->print_page(page-1, viewport);
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);
viewport.x += tileW;
paperPos.x += tileW;
}
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.