KiCad PCB EDA Suite
Loading...
Searching...
No Matches
unix/printing.cpp
Go to the documentation of this file.
1/*
2* This program source code file is part of KiCad, a free EDA CAD application.
3*
4* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5*
6* This program is free software: you can redistribute it and/or modify it
7* under the terms of the GNU General Public License as published by the
8* Free Software Foundation, either version 3 of the License, or (at your
9* option) any later version.
10*
11* This program is distributed in the hope that it will be useful, but
12* WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14* General Public License for more details.
15*
16* You should have received a copy of the GNU General Public License
17* along with this program. If not, see <https://www.gnu.org/licenses/>.
18*/
19#include <printing.h>
20
21#include <poppler/glib/poppler.h>
22#include <gtk/gtk.h>
23#include <unistd.h>
24#include <algorithm>
25#include <memory>
26
27#include <wx/print.h>
28#include <wx/cmndata.h>
29
30#if wxUSE_GTKPRINT
31#include <wx/gtk/print.h>
32#endif
33
34namespace
35{
36struct PrintData
37{
38 PopplerDocument* doc;
39 bool fit_to_page;
40
41 PrintData( PopplerDocument* d ) :
42 doc( d ),
43 fit_to_page( true )
44 {
45 }
46};
47
48void draw_page( GtkPrintOperation* operation, GtkPrintContext* context, gint page_nr, gpointer user_data )
49{
50 PrintData* print_data = static_cast<PrintData*>( user_data );
51
52 if( !print_data || !print_data->doc )
53 return;
54
55 PopplerPage* page = poppler_document_get_page( print_data->doc, page_nr );
56
57 if( !page ) return;
58
59 auto cleanup_page = std::unique_ptr<PopplerPage, decltype( &g_object_unref )>( page, &g_object_unref );
60
61 cairo_t* cr = gtk_print_context_get_cairo_context( context );
62
63 if( !cr ) return;
64
65 // Get page dimensions
66 double page_width, page_height;
67 poppler_page_get_size( page, &page_width, &page_height );
68
69 // Get print context dimensions
70 double print_width = gtk_print_context_get_width( context );
71 double print_height = gtk_print_context_get_height( context );
72
73 cairo_save( cr );
74 auto cleanup_cairo = std::unique_ptr<cairo_t, decltype( &cairo_restore )>( cr, &cairo_restore );
75
76 if( print_data->fit_to_page )
77 {
78 // Calculate scaling to fit page while maintaining aspect ratio
79 double scale_x = print_width / page_width;
80 double scale_y = print_height / page_height;
81 double scale = std::min( scale_x, scale_y );
82
83 // Center the page
84 double scaled_width = page_width * scale;
85 double scaled_height = page_height * scale;
86 double offset_x = ( print_width - scaled_width ) / 2.0;
87 double offset_y = ( print_height - scaled_height ) / 2.0;
88
89 // Apply transformations
90 cairo_translate( cr, offset_x, offset_y );
91 cairo_scale( cr, scale, scale );
92 }
93
94 // Set white background
95 cairo_set_source_rgb( cr, 1.0, 1.0, 1.0 );
96 cairo_paint( cr );
97
98 // Render the page
99 poppler_page_render( page, cr );
100}
101
102void begin_print_callback( GtkPrintOperation* operation, GtkPrintContext* context, gpointer user_data )
103{
104 PrintData* print_data = static_cast<PrintData*>( user_data );
105 if( !print_data || !print_data->doc )
106 {
107 gtk_print_operation_cancel( operation );
108 return;
109 }
110
111 int num_pages = poppler_document_get_n_pages( print_data->doc );
112 if( num_pages <= 0 )
113 {
114 gtk_print_operation_cancel( operation );
115 return;
116 }
117
118 gtk_print_operation_set_n_pages( operation, num_pages );
119}
120
121void request_page_setup_callback( GtkPrintOperation* operation, GtkPrintContext* context, gint page_nr,
122 GtkPageSetup* setup, gpointer user_data )
123{
124 PrintData* print_data = static_cast<PrintData*>( user_data );
125
126 if( !print_data || !print_data->doc )
127 return;
128
129 PopplerPage* page = poppler_document_get_page( print_data->doc, page_nr );
130
131 if( !page )
132 return;
133
134 // Get page dimensions to determine orientation
135 double page_width, page_height;
136 poppler_page_get_size( page, &page_width, &page_height );
137
138 // Set orientation based on page dimensions
139 GtkPageOrientation orientation =
140 ( page_width > page_height ) ? GTK_PAGE_ORIENTATION_LANDSCAPE : GTK_PAGE_ORIENTATION_PORTRAIT;
141 gtk_page_setup_set_orientation( setup, orientation );
142
143 g_object_unref( page );
144}
145} // namespace
146
147namespace KIPLATFORM
148{
149namespace PRINTING
150{
151 PRINT_RESULT PrintPDF( const std::string& aFile, bool fit_to_page )
152 {
153 // Check file accessibility
154 if( access( aFile.c_str(), R_OK ) != 0 )
156
157 // Create file URI
158 gchar* uri = g_filename_to_uri( aFile.c_str(), NULL, NULL );
159 if( !uri )
161
162 // Load the PDF document
163 GError* error = NULL;
164 PopplerDocument* doc = poppler_document_new_from_file( uri, NULL, &error );
165 g_free( uri );
166
167 if( error )
168 {
169 g_error_free( error );
171 }
172
173 if( !doc )
175
176 auto cleanup_doc = std::unique_ptr<PopplerDocument, decltype(&g_object_unref)>(doc, &g_object_unref);
177
178 // Check if document has pages
179 int num_pages = poppler_document_get_n_pages( doc );
180
181 if( num_pages <= 0 )
183
184 // Create print data
185 PrintData print_data( doc );
186 print_data.fit_to_page = fit_to_page;
187
188 // Create print operation
189 GtkPrintOperation* op = gtk_print_operation_new();
190
191 if( !op )
193
194 auto cleanup_op = std::unique_ptr<GtkPrintOperation, decltype( &g_object_unref )>( op, &g_object_unref );
195
196 // Set up print operation properties
197 gtk_print_operation_set_use_full_page( op, FALSE );
198 gtk_print_operation_set_unit( op, GTK_UNIT_POINTS );
199
200 // Connect callbacks
201 g_signal_connect( op, "begin-print", G_CALLBACK( begin_print_callback ), &print_data );
202 g_signal_connect( op, "draw-page", G_CALLBACK( draw_page ), &print_data );
203 g_signal_connect( op, "request-page-setup", G_CALLBACK( request_page_setup_callback ), &print_data );
204
205 // Run the print operation
206 error = NULL;
207 GtkPrintOperationResult result =
208 gtk_print_operation_run( op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL, &error );
209
210 // Handle errors and determine result
211 PRINT_RESULT return_result;
212
213 if( error )
214 {
215 g_error_free( error );
216 return_result = PRINT_RESULT::FAILED_TO_PRINT;
217 }
218 else
219 {
220 switch( result )
221 {
222 case GTK_PRINT_OPERATION_RESULT_APPLY: return_result = PRINT_RESULT::OK; break;
223 case GTK_PRINT_OPERATION_RESULT_CANCEL: return_result = PRINT_RESULT::CANCELLED; break;
224 case GTK_PRINT_OPERATION_RESULT_ERROR:
225 default: return_result = PRINT_RESULT::FAILED_TO_PRINT; break;
226 }
227 }
228
229 return return_result;
230 }
231
232 PRINT_RESULT PrintPDF( const std::string& aFile )
233 {
234 return PrintPDF( aFile, true );
235 }
236
237 void ResetPrintToFilePath( wxPrintData& aData )
238 {
239 aData.SetFilename( wxEmptyString );
240
241#if wxUSE_GTKPRINT
242 // The temporary destination lives in the native GtkPrintSettings output URI, which the
243 // wx-level filename does not track, so it has to be unset directly.
244 wxGtkPrintNativeData* nativeData = dynamic_cast<wxGtkPrintNativeData*>( aData.GetNativeData() );
245
246 if( nativeData && nativeData->GetPrintConfig() )
247 gtk_print_settings_unset( nativeData->GetPrintConfig(), GTK_PRINT_SETTINGS_OUTPUT_URI );
248#endif
249 }
250
251} // namespace PRINTING
252} // namespace KIPLATFORM
void ResetPrintToFilePath(wxPrintData &aData)
Clear any leftover "print to file" destination from aData.
PRINT_RESULT PrintPDF(const std::string &aFile)
const int scale
wxString result
Test unit parsing edge cases and error handling.