/** \file print.c * Printing functions using GTK's print API */ /* XTrkCad - Model Railroad CAD * Copyright (C) 2015 Martin Fischer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #ifdef HAVE_MALLOC_H #include #endif #include #include #include #include "gtkint.h" #include #include #include "wlib.h" #include "i18n.h" extern wDrawColor wDrawColorWhite; extern wDrawColor wDrawColorBlack; /***************************************************************************** * * MACROS * */ #define PRINT_PORTRAIT (0) #define PRINT_LANDSCAPE (1) #define PPI (72.0) #define P2I( P ) ((P)/PPI) #define CENTERMARK_LENGTH (60) /**< size of cross marking center of circles */ #define DASH_LENGTH (8.0) /**< length of single dash */ #define PAGESETTINGS "xtrkcad.page" /**< filename for page settings */ #define PRINTSETTINGS "xtrkcad.printer" /**< filename for printer settings */ /***************************************************************************** * * VARIABLES * */ static GtkPrintSettings *settings; /**< current printer settings */ static GtkPageSetup *page_setup; /**< current paper settings */ static GtkPrinter *selPrinter; /**< printer selected by user */ static GtkPrintJob *curPrintJob; /**< currently active print job */ extern struct wDraw_t psPrint_d; static wBool_t printContinue; /**< control print job, FALSE for cancelling */ static wIndex_t pageCount; /**< unused, could be used for progress indicator */ static wIndex_t totalPageCount; /**< unused, could be used for progress indicator */ static double paperWidth; /**< physical paper width */ static double paperHeight; /**< physical paper height */ static double tBorder; /**< top margin */ static double rBorder; /**< right margin */ static double lBorder; /**< left margin */ static double bBorder; /**< bottom margin */ static long printFormat = PRINT_LANDSCAPE; /***************************************************************************** * * FUNCTIONS * */ static void WlibGetPaperSize( void ); /** * Initialize printer und paper selection using the saved settings * * \param op IN print operation to initialize. If NULL only the global * settings are loaded. */ void WlibApplySettings( GtkPrintOperation *op ) { gchar *filename; GError *err = NULL; GtkWidget *dialog; filename = g_build_filename( wGetAppWorkDir(), PRINTSETTINGS, NULL ); if( !(settings = gtk_print_settings_new_from_file( filename, &err ))) { if( err->code != G_FILE_ERROR_NOENT ) { // ignore file not found error as defaults will be used dialog = gtk_message_dialog_new (GTK_WINDOW (gtkMainW->gtkwin), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, err->message); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } else { // create default print settings settings = gtk_print_settings_new(); } g_error_free (err); } g_free( filename ); if (settings && op ) gtk_print_operation_set_print_settings (op, settings); err = NULL; filename = g_build_filename( wGetAppWorkDir(), PAGESETTINGS, NULL ); if( !(page_setup = gtk_page_setup_new_from_file( filename, &err ))) { // ignore file not found error as defaults will be used if( err->code != G_FILE_ERROR_NOENT ) { dialog = gtk_message_dialog_new (GTK_WINDOW (gtkMainW->gtkwin), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, err->message); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } else { page_setup = gtk_page_setup_new(); } g_error_free (err); } else { // on success get the paper dimensions WlibGetPaperSize(); } g_free( filename ); if( page_setup && op ) gtk_print_operation_set_default_page_setup (op, page_setup); } /** * Save the printer settings. If op is not NULL the settings are retrieved * from the print operation. Otherwise the state of the globals is saved. * * \param op IN printer operation. If NULL the glabal variables are used */ void WlibSaveSettings( GtkPrintOperation *op ) { GError *err = NULL; gchar *filename; GtkWidget *dialog; if( op ) { if (settings != NULL) g_object_unref (settings); settings = g_object_ref (gtk_print_operation_get_print_settings (op)); } filename = g_build_filename( wGetAppWorkDir(), PRINTSETTINGS, NULL ); if( !gtk_print_settings_to_file( settings, filename, &err )) { dialog = gtk_message_dialog_new (GTK_WINDOW (gtkMainW->gtkwin), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, err->message); g_error_free (err); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } g_free( filename ); if( op ) { if (page_setup != NULL) g_object_unref (page_setup); page_setup = g_object_ref (gtk_print_operation_get_default_page_setup (op)); } filename = g_build_filename( wGetAppWorkDir(), PAGESETTINGS, NULL ); if( !gtk_page_setup_to_file( page_setup, filename, &err )) { dialog = gtk_message_dialog_new (GTK_WINDOW (gtkMainW->gtkwin), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, err->message); g_error_free (err); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } g_free( filename ); } /** * Page setup function. Previous settings are loaded and the setup * dialog is shown. The settings are saved after the dialog ends. * * \param callback IN unused */ void wPrintSetup( wPrintSetupCallBack_p callback ) { GtkPageSetup *new_page_setup; gchar *filename; GError *err; GtkWidget *dialog; WlibApplySettings( NULL ); new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (gtkMainW->gtkwin), page_setup, settings); if (page_setup) g_object_unref (page_setup); page_setup = new_page_setup; WlibGetPaperSize(); WlibSaveSettings( NULL ); } /***************************************************************************** * * BASIC PRINTING * */ /** * set the current line type for printing operations * * \param lineWidth IN new line width * \param lineType IN flag for line type (dashed or full) * \param opts IN unused * \return */ static void setLineType( double lineWidth, wDrawLineType_e lineType, wDrawOpts opts ) { cairo_t *cr = psPrint_d.printContext; double dashLength = DASH_LENGTH; if (lineWidth < 0.0) { lineWidth = P2I(-lineWidth)*2.0; } // make sure that there is a minimum line width used if ( lineWidth == 0.0 ) lineWidth = 0.1; cairo_set_line_width( cr, lineWidth ); if (lineType == wDrawLineDash) cairo_set_dash( cr, &dashLength, 1, 0.0 ); else cairo_set_dash( cr, NULL, 0, 0.0 ); } /** * set the color for the following print operations * * \param color IN the new color * \return */ static void psSetColor( wDrawColor color ) { cairo_t *cr = psPrint_d.printContext; GdkColor* const gcolor = gtkGetColor(color, TRUE); cairo_set_source_rgb(cr, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0); } /** * Print a straight line * * \param x0, y0 IN starting point in pixels * \param x1, y1 IN ending point in pixels * \param width line width * \param lineType * \param color color * \param opts ? */ void psPrintLine( wPos_t x0, wPos_t y0, wPos_t x1, wPos_t y1, wDrawWidth width, wDrawLineType_e lineType, wDrawColor color, wDrawOpts opts ) { if (color == wDrawColorWhite) return; if (opts&wDrawOptTemp) return; psSetColor(color); setLineType( width, lineType, opts ); cairo_move_to( psPrint_d.printContext, x0, y0 ); cairo_line_to( psPrint_d.printContext, x1, y1 ); cairo_stroke( psPrint_d.printContext ); } /** * Print an arc around a specified center * * \param x0, y0 IN center of arc * \param r IN radius * \param angle0, angle1 IN start and end angle * \param drawCenter draw marking for center * \param width line width * \param lineType * \param color color * \param opts ? */ void psPrintArc( wPos_t x0, wPos_t y0, wPos_t r, double angle0, double angle1, wBool_t drawCenter, wDrawWidth width, wDrawLineType_e lineType, wDrawColor color, wDrawOpts opts ) { cairo_t *cr = psPrint_d.printContext; if (color == wDrawColorWhite) return; if (opts&wDrawOptTemp) return; psSetColor(color); setLineType(width, lineType, opts); if (angle1 >= 360.0) angle1 = 359.999; angle1 = 90.0-(angle0+angle1); while (angle1 < 0.0) angle1 += 360.0; while (angle1 >= 360.0) angle1 -= 360.0; angle0 = 90.0-angle0; while (angle0 < 0.0) angle0 += 360.0; while (angle0 >= 360.0) angle0 -= 360.0; // draw the curve cairo_arc( cr, x0, y0, r, angle1 * M_PI / 180.0, angle0 * M_PI / 180.0 ); if( drawCenter ) { // draw crosshair for center of curve cairo_move_to( cr, x0 - CENTERMARK_LENGTH / 2, y0 ); cairo_line_to( cr, x0 + CENTERMARK_LENGTH / 2, y0 ); cairo_move_to( cr, x0, y0 - CENTERMARK_LENGTH / 2 ); cairo_line_to( cr, x0, y0 + CENTERMARK_LENGTH / 2 ); } cairo_stroke( psPrint_d.printContext ); } /** * Print a filled rectangle * * \param x0, y0 IN top left corner * \param x1, y1 IN bottom right corner * \param color IN fill color * \param opts IN options * \return */ void psPrintFillRectangle( wPos_t x0, wPos_t y0, wPos_t x1, wPos_t y1, wDrawColor color, wDrawOpts opts ) { cairo_t *cr = psPrint_d.printContext; double width = x0 - x1; double height = y0 - y1; if (color == wDrawColorWhite) return; if (opts&wDrawOptTemp) return; psSetColor(color); cairo_rectangle( cr, x0, y0, width, height ); cairo_fill( cr ); } /** * Print a filled polygon * * \param p IN a list of x and y coordinates * \param cnt IN the number of points * \param color IN fill color * \param opts IN options * \return */ void psPrintFillPolygon( wPos_t p[][2], int cnt, wDrawColor color, wDrawOpts opts ) { int inx; cairo_t *cr = psPrint_d.printContext; if (color == wDrawColorWhite) return; if (opts&wDrawOptTemp) return; psSetColor(color); cairo_move_to( cr, p[ 0 ][ 0 ], p[ 0 ][ 1 ] ); for (inx=0; inxgtkwin)); // load the settings WlibApplySettings( NULL ); // and apply them to the printer dialog gtk_print_unix_dialog_set_settings( (GtkPrintUnixDialog *)printDialog, settings ); gtk_print_unix_dialog_set_page_setup( (GtkPrintUnixDialog *)printDialog, page_setup ); res = gtk_dialog_run( (GtkDialog *)printDialog ); if( res == GTK_RESPONSE_OK ) { selPrinter = gtk_print_unix_dialog_get_selected_printer( (GtkPrintUnixDialog *)printDialog ); if( settings ) g_object_unref (settings); settings = gtk_print_unix_dialog_get_settings( (GtkPrintUnixDialog *)printDialog ); if( page_setup ) g_object_unref( page_setup ); page_setup = gtk_print_unix_dialog_get_page_setup( (GtkPrintUnixDialog *)printDialog ); curPrintJob = gtk_print_job_new( title, selPrinter, settings, page_setup ); psPrint_d.curPrintSurface = gtk_print_job_get_surface( curPrintJob, NULL ); psPrint_d.printContext = cairo_create( psPrint_d.curPrintSurface ); //update the paper dimensions WlibGetPaperSize(); /* for the file based surfaces the resolution is 72 dpi (see documentation) */ surface_type = cairo_surface_get_type( psPrint_d.curPrintSurface ); if( surface_type == CAIRO_SURFACE_TYPE_PDF || surface_type == CAIRO_SURFACE_TYPE_PS || surface_type == CAIRO_SURFACE_TYPE_SVG ) psPrint_d.dpi = 72; else psPrint_d.dpi = (double)gtk_print_settings_get_resolution( settings ); // in XTrackCAD 0,0 is top left, in cairo bottom left. This is // corrected via the following transformations. // also the translate makes sure that the drawing is rendered // within the paper margins cairo_scale( psPrint_d.printContext, 1.0, -1.0 ); cairo_translate( psPrint_d.printContext, lBorder * psPrint_d.dpi, -(paperHeight-bBorder) *psPrint_d.dpi ); WlibSaveSettings( NULL ); } gtk_widget_destroy (printDialog); if (copiesP) *copiesP = 1; printContinue = TRUE; if( res != GTK_RESPONSE_OK ) return FALSE; else return TRUE; } /** * Callback for job finished event. Destroys the cairo context. * * \param job IN unused * \param data IN unused * \param err IN if != NULL, an error dialog ist displayed * \return */ void doPrintJobFinished( GtkPrintJob *job, void *data, GError *err ) { GtkWidget *dialog; cairo_destroy( psPrint_d.printContext ); if( err ) { dialog = gtk_message_dialog_new (GTK_WINDOW (gtkMainW->gtkwin), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, err->message); } } /** * Finish the print operation * \return */ void wPrintDocEnd( void ) { cairo_surface_finish( psPrint_d.curPrintSurface ); gtk_print_job_send( curPrintJob, doPrintJobFinished, NULL, NULL ); // wWinShow( printAbortW, FALSE ); } wBool_t wPrintQuit( void ) { return FALSE; } wBool_t wPrintInit( void ) { return TRUE; }