Printing

This chapter includes:

Overview

Printing and drawing are the same in Photon—the difference depends on the draw context, a data structure that defines where the draw stream (i.e. the draw events) flows:

To print in Photon:

  1. Create a print context by calling PpCreatePC().
  2. Set up the print context automatically via the PtPrintSel widget, or programmatically via PpSetPC().
  3. Initialize the print job by calling PpStartJob().
  4. Any time after PpStartJob() is called, make the print context “active” by calling PpContinueJob(). When a print context is active, anything that's drawn via PpPrintWidget() or Pg* calls, including widgets, is directed to the file opened by the print context during the PpStartJob() call.
  5. Insert page breaks, as required, by calling PpPrintNewPage().
  6. The print context can be made inactive without terminating the current print job by calling PpSuspendJob(), or by calling PpContinueJob() with a different print context. To resume the print job from where you left off, call PpContinueJob().
  7. Complete the print job by calling PpEndJob().
  8. When your application doesn't need to print anything else, call PpReleasePC() to free the print context.

Print contexts

A print context is a PpPrintContext_t structure whose members control how printing is to be done. For information about what's in a print context, see the Photon Library Reference.


Note: Never directly access the members of a PpPrintContext_t structure; use PpGetPC() to extract members, and PpSetPC() to change them.

Creating a print context

The first step to printing in Photon is to create a print context by calling PpCreatePC():

PpPrintContext_t *pc;

pc = PpCreatePC();

Modifying a print context

Once the print context is created, you must set it up properly for your printer and the options (orientation, paper size, etc.) you want to use. You can do this by calling:

These functions are described in the Photon Library Reference.

You can also use PtPrintSel (see the Photon Widget Reference).

You can get a list of available printers by calling PpLoadPrinterList(). When you're finished with the list, call PpFreePrinterList().

Starting a print job

If you're using an application that needs to know anything about the print context, you can use PpGetPC() to get the appropriate information. For example, you might need to know the selected orientation (in order to orient your widgets properly). If you need to know the size of the margins, you can call PpGetCanvas().

Before printing, you must set the source size or resolution. For example:

When setting the source size, take the nonprintable area of the printer into account. All printers have a margin around the page that they won't print on, even if the page margins are set to 0. Therefore, the size set above is actually a bit larger than the size of a page, and the font will be scaled down to fit on the printable part of the page.

In the following example, the page size and nonprintable area are taken into account to give the proper source size and text height. Try this, and measure the output to prove the font is 1″ high from ascender to descender:

#include <stdio.h>
#include <stdlib.h>
#include <Pt.h>

PtWidget_t *label, *window;
PpPrintContext_t *pc;

int quit_cb (PtWidget_t *widget, void *data, 
             PtCallbackInfo_t *cbinfo )
{
    exit (EXIT_SUCCESS);
    return (Pt_CONTINUE);
}

int print_cb (PtWidget_t *widget, void *data, 
              PtCallbackInfo_t *cbinfo )
{
    int action;
    PhDim_t size;
    PhRect_t const *rect;
    PhDim_t const *dim;

    action = PtPrintSelection(window, NULL, 
                              "Demo Print Selector", 
                              pc, Pt_PRINTSEL_DFLT_LOOK);
    if (action != Pt_PRINTSEL_CANCEL)
    {
        /* Get the nonprintable area and page size. Both are in 
           1/1000ths of an inch. */

        PpGetPC(pc, Pp_PC_NONPRINT_MARGINS, &rect);
        PpGetPC(pc, Pp_PC_PAPER_SIZE, &dim);
        size.w = ((dim->w  - 
                  (rect->ul.x + rect->lr.x)) * 72) / 1000;
        size.h = ((dim->h  - 
                  (rect->ul.y + rect->lr.y)) * 72) / 1000;

        /* Set the source size. */
        PpSetPC( pc, Pp_PC_SOURCE_SIZE, &size, 0);

        /* Start printing the label. */
        PpStartJob(pc);
        PpContinueJob(pc);

        /* Damage the widget. */
        PtDamageWidget(label);
        PtFlush();

        /* Close the PC. */
        PpSuspendJob(pc);
        PpEndJob(pc);
    }
    return (Pt_CONTINUE);
}

int main(int argc, char *argv[])
{
    PtArg_t args[10];
    PtWidget_t *print, *quit;
    PhDim_t   win_dim = { 400, 200 };
    PhPoint_t lbl_pos = {0, 0};
    PhArea_t print_area = { {130, 170}, {60, 20} };
    PhArea_t quit_area = { {210, 170}, {60, 20} };
    PtCallback_t callbacks[2] = { {print_cb, NULL}, 
                                  {quit_cb, NULL} };

    if (PtInit(NULL) == -1)
        PtExit(EXIT_FAILURE);

    /* Create the main window. */
    PtSetArg (&args[0], Pt_ARG_DIM, &win_dim, 0);
    PtSetArg (&args[1], Pt_ARG_WINDOW_TITLE, 
              "Print Example", 0);

    if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT,
                                 1, args)) == NULL)
        PtExit (EXIT_FAILURE);

    /* Create a print context. */
    pc = PpCreatePC();

    /* Create a label to be printed. */
    PtSetArg (&args[0], Pt_ARG_POS, &lbl_pos, 0);
    PtSetArg (&args[1], Pt_ARG_TEXT_STRING, 
              "I am 1 inch high", 0);
    PtSetArg (&args[2], Pt_ARG_TEXT_FONT, "swiss72", 0);
    PtSetArg (&args[3], Pt_ARG_MARGIN_HEIGHT, 0, 0);
    PtSetArg (&args[4], Pt_ARG_MARGIN_WIDTH, 0, 0);
    PtSetArg (&args[5], Pt_ARG_BEVEL_WIDTH, 0, 0);
    label = PtCreateWidget (PtLabel, window, 6, args);

    /* Create the print button. */
    PtSetArg(&args[0], Pt_ARG_AREA, &print_area, 0);
    PtSetArg(&args[1], Pt_ARG_TEXT_STRING, "Print", 0);
    PtSetArg(&args[2], Pt_CB_ACTIVATE, &callbacks[0], 0);
    print = PtCreateWidget (PtButton, window, 3, args);

    /* Create the quit button. */
    PtSetArg(&args[0], Pt_ARG_AREA, &quit_area, 0);
    PtSetArg(&args[1], Pt_ARG_TEXT_STRING, "Quit", 0);
    PtSetArg(&args[2], Pt_CB_ACTIVATE, &callbacks[1], 0);
    quit = PtCreateWidget (PtButton, window, 3, args);

    PtRealizeWidget(window);
    PtMainLoop();
    return (EXIT_SUCCESS);
}

You should also set the source offset, the upper left corner of what's to be printed. For example, if you have a button drawn at (20, 20) from the top left of a pane and you want it to be drawn at (0, 0) on the page, set the source offset to (20, 20). Any other widgets are drawn relative to their position from this widget's origin. A widget at (40, 40) will be drawn at (20, 20) on the page. The code is as follows:

PhPoint_t offset = {20, 20};
...
PpSetPC( pc, Pp_PC_SOURCE_OFFSET, &offset, 0 );

Once the source size and offset have been set, you can start printing:

PpStartJob(pc);
PpContinueJob(pc);

PpStartJob() sets up the print context for printing and PpContinueJob() makes the print context active, causing all Photon draw commands to be redirected to the destination specified in the print context.

Printing the desired widgets

After you've made the print context active, you can start printing widgets and so on. This can be done by calling any combination of the following:


Note: If you want to print all the contents of a widget that scrolls, you'll need to do some special preparations. See Printing scrolling widgets below.

Printing a new page

You can force a page break at any point by calling PpPrintNewPage():

PpPrintNewPage(pc);

Note that once you call PpStartJob(), any changes to the print context take effect after the next call to PpPrintNewPage().

Photon assumes that the page numbers increase by one. If this isn't the case, manually set the Pp_PC_PAGE_NUM member of the print context to the correct page number. Don't make the page number decrease because the print drivers might not work properly.

Printing widgets that scroll

If you want to print all the contents of a widget that scrolls, you need some special processing:

PtList

The only way to make a PtList print (or draw) all the items is by resizing it to be the total height of all the items. The easiest way is probably by using the resize policy:


Note: This will work only if the total height is smaller than 65K pixels.

  1. Open and start the print context.
  2. Get the current resize flags (Pt_ARG_RESIZE_FLAGS) for the PtList widget.
  3. Change the resize flags to Pt_RESIZE_XY_ALWAYS, to make the list resize to fit all of its text.
  4. Call PpPrintWidget() for the widget or parent widget.
  5. Reset the resize flags for the PtList widget.
  6. Stop and close the print context.

PtMultiText

To print a PtMultiText widget's entire text, breaking the output into pages:

  1. Create another multitext widget — let's call it the print widget — that isn't visible to the user (i.e. hide it behind the user's multitext widget).
  2. Get the printer settings for printing: the orientation, page size, and the margins.
  3. Adjust the printer settings for what you want and then use PpSetPC() to set them.
  4. Set the print multitext widget's margins to match those of the printer that you just set.
  5. Use PpStartJob() to start your print job.
  6. Get the user's multitext widget resources (i.e. text, fonts, number of lines) and set the print multitext widget's resources to match them.
  7. Go through the user's multitext and get the attributes for each line (color, font, tabs, etc) and set the print multitext widget's attributes accordingly.
  8. Once you've set all of the attributes to match, specify the top line of the print multitext widget. This positions the widget to start printing.
  9. Get the number of lines that are completely visible in the print multitext widget, as well as the total number of lines.
  10. Use PpContinueJob(), PpPrintWidget(), and PpSuspendJob() to print the current page.
  11. Delete the lines that you just printed from the print multitext widget. Doing this causes the next group of lines that you want to print to become the visible lines of the widget.
  12. Call PpPrintNewPage() to insert a page break.
  13. Continue printing pages and deleting the visible lines until you've reached the end of the text in the print multitext widget.

PtScrollArea

For a PtScrollArea, you need to print its virtual canvas, which is where all widgets created within or moved to a scroll area are placed:

  1. Get a pointer to the virtual canvas by calling:
    PtValidParent( ABW_Scroll_area, PtWidget );
              
  2. Get the area (Pt_ARG_AREA) of the virtual canvas, and use its size member as the source size in the print context.
  3. Set the print context's source offset to:
    PtWidgetOffset( PtValidParent( ABW_Scroll_area, 
                                   PtWidget ));
              
  4. Print the scroll area's virtual canvas by calling:
    PpPrintWidget( pc, PtValidParent( ABW_Scroll_area, 
                                      PtWidget ), 
                   NULL, NULL, 0);
              

Suspending and resuming a print job

To suspend a print job and direct all draw events back to the graphics driver at any point after calling PpStartJob(), call:

PpSuspendJob( pc );

To resume a print job, reactivating the print context, causing draw events to be directed towards the destination specified in the print context, call:

PpContinueJob( pc );

Ending a print job

When you're finished printing your widgets, the print context must be deactivated and closed. This is done by calling:

PpSuspendJob(pc);
PpEndJob(pc);

All draw events will be directed to the graphics driver.


Note: You can reuse the print context for new print jobs, eliminating the need to create and initialize it again.

Freeing the print context

When printing is complete and you no longer need the print context, you can free it, which in turn frees any resources used by it.

If you want to remember any information from the print context for future use, save it by calling PpGetPC() before freeing the print context. For example:

short const *orientation;
...
PpGetPC( pc, Pp_PC_ORIENTATION, &orientation );

To free a print context, call:

PpReleasePC( pc );

Example

This example creates an application with a main window, and a pane with a few widgets on it. When you press the Print button, a Print Selection Dialog appears. When you select this dialog's Print or Preview button, the pane is “drawn” on the printer.

#include <stdio.h>
#include <stdlib.h>
#include <Pt.h>

PtWidget_t *pane, *window;
PpPrintContext_t *pc;

int quit_cb ( PtWidget_t *widget, void *data, 
              PtCallbackInfo_t *cbinfo)
{
    PpReleasePC (pc);
    exit (EXIT_SUCCESS);
    return (Pt_CONTINUE);
}

int print_cb ( PtWidget_t *widget, void *data, 
               PtCallbackInfo_t *cbinfo)
{
    int action;

    /* You could make these calls to PpSetPC() right
       after creating the print context. Having it here
       lets you reuse the print context. */
    PhDim_t size = { 850, 1100 };
    PhDim_t size2 = { 200, 150 };

    /* Set the source resolution to be proportional to 
       the size of a page. */
    PpSetPC(pc, Pp_PC_SOURCE_SIZE, &size, 0);

    /* Uncomment this to set the source size to be the size
       of the widget. The widget will be scaled when printed. */
    /* PpSetPC(pc, Pp_PC_SOURCE_SIZE, &size2, 0); */

    action = PtPrintSelection(window, NULL, 
                 "Demo Print Selector", pc, 
                 Pt_PRINTSEL_DFLT_LOOK);
    if (action != Pt_PRINTSEL_CANCEL)
    {
        /* Start printing the pane. Note that we're using
           the same print context for all print jobs. */
        PpStartJob(pc);
        PpContinueJob(pc);

        /* Print the widget. */
        PpPrintWidget(pc, pane, NULL, NULL, 0);

        /* Close the print context. */
        PpSuspendJob(pc);
        PpEndJob(pc);
    }

    return (Pt_CONTINUE);
}

int main(int argc, char *argv[])
{
    PtArg_t args[4];
    PtWidget_t *print, *quit;
    PhDim_t  win_dim = { 200, 200 };
    PhArea_t pane_area = { {0, 0}, {200, 150} };
    PhArea_t print_area = { {30, 170}, {60, 20} };
    PhArea_t quit_area = { {110, 170}, {60, 20} };
    PhArea_t cir_area = { {35, 20}, {130, 110} };
    PhArea_t cir2_area = { {67, 40}, {20, 20} };
    PhArea_t cir3_area = { {110, 40}, {20, 20} };
    PhArea_t cir4_area = { {85, 80}, {30, 30} };
    PtCallback_t callbacks[2] = { {print_cb, NULL}, 
                                  {quit_cb, NULL} };

    if (PtInit(NULL) == -1)
        PtExit(EXIT_FAILURE);

    /* Create the main window. */
    PtSetArg (&args[0], Pt_ARG_DIM, &win_dim, 0);
    PtSetArg (&args[1], Pt_ARG_WINDOW_TITLE, 
              "Print Example", 0);
    if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT,
                                 2, args)) == NULL)
        PtExit(EXIT_FAILURE);

    /* Create a print context. */
    pc = PpCreatePC();

    /* Create the pane to be printed. */
    PtSetArg (&args[0], Pt_ARG_AREA, &pane_area, 0);
    pane = PtCreateWidget (PtPane, window, 1, args);

    /* put some stuff in the pane to be printed. */
    PtSetArg (&args[0], Pt_ARG_AREA, &cir_area, 0);
    PtCreateWidget (PtEllipse, pane, 1, args);

    PtSetArg (&args[0], Pt_ARG_AREA, &cir2_area, 0);
    PtSetArg (&args[1], Pt_ARG_FILL_COLOR, Pg_BLACK, 0);
    PtCreateWidget (PtEllipse, pane, 2, args);

    PtSetArg (&args[0], Pt_ARG_AREA, &cir3_area, 0);
    PtSetArg (&args[1], Pt_ARG_FILL_COLOR, Pg_BLACK, 0);
    PtCreateWidget (PtEllipse, pane, 2, args);

    PtSetArg (&args[0], Pt_ARG_AREA, &cir4_area, 0);
    PtCreateWidget (PtEllipse, pane, 1, args);

    /* Create the print button. */
    PtSetArg(&args[0], Pt_ARG_AREA, &print_area, 0);
    PtSetArg(&args[1], Pt_ARG_TEXT_STRING, "Print", 0);
    PtSetArg(&args[2], Pt_CB_ACTIVATE, &callbacks[0], 0);
    print = PtCreateWidget (PtButton, window, 3, args);

    /* Create the quit button. */
    PtSetArg(&args[0], Pt_ARG_AREA, &quit_area, 0);
    PtSetArg(&args[1], Pt_ARG_TEXT_STRING, "Quit", 0);
    PtSetArg(&args[2], Pt_CB_ACTIVATE, &callbacks[1], 0);
    quit = PtCreateWidget (PtButton, window, 3, args);

    PtRealizeWidget(window);
    PtMainLoop();
    return (EXIT_SUCCESS);
}