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 along
17* with this program. If not, see <http://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
27namespace
28{
29struct PrintData
30{
31 PopplerDocument* doc;
32 bool fit_to_page;
33
34 PrintData( PopplerDocument* d ) :
35 doc( d ),
36 fit_to_page( true )
37 {
38 }
39};
40
41void draw_page( GtkPrintOperation* operation, GtkPrintContext* context, gint page_nr, gpointer user_data )
42{
43 PrintData* print_data = static_cast<PrintData*>( user_data );
44
45 if( !print_data || !print_data->doc )
46 return;
47
48 PopplerPage* page = poppler_document_get_page( print_data->doc, page_nr );
49
50 if( !page ) return;
51
52 auto cleanup_page = std::unique_ptr<PopplerPage, decltype( &g_object_unref )>( page, &g_object_unref );
53
54 cairo_t* cr = gtk_print_context_get_cairo_context( context );
55
56 if( !cr ) return;
57
58 // Get page dimensions
59 double page_width, page_height;
60 poppler_page_get_size( page, &page_width, &page_height );
61
62 // Get print context dimensions
63 double print_width = gtk_print_context_get_width( context );
64 double print_height = gtk_print_context_get_height( context );
65
66 cairo_save( cr );
67 auto cleanup_cairo = std::unique_ptr<cairo_t, decltype( &cairo_restore )>( cr, &cairo_restore );
68
69 if( print_data->fit_to_page )
70 {
71 // Calculate scaling to fit page while maintaining aspect ratio
72 double scale_x = print_width / page_width;
73 double scale_y = print_height / page_height;
74 double scale = std::min( scale_x, scale_y );
75
76 // Center the page
77 double scaled_width = page_width * scale;
78 double scaled_height = page_height * scale;
79 double offset_x = ( print_width - scaled_width ) / 2.0;
80 double offset_y = ( print_height - scaled_height ) / 2.0;
81
82 // Apply transformations
83 cairo_translate( cr, offset_x, offset_y );
84 cairo_scale( cr, scale, scale );
85 }
86
87 // Set white background
88 cairo_set_source_rgb( cr, 1.0, 1.0, 1.0 );
89 cairo_paint( cr );
90
91 // Render the page
92 poppler_page_render( page, cr );
93}
94
95void begin_print_callback( GtkPrintOperation* operation, GtkPrintContext* context, gpointer user_data )
96{
97 PrintData* print_data = static_cast<PrintData*>( user_data );
98 if( !print_data || !print_data->doc )
99 {
100 gtk_print_operation_cancel( operation );
101 return;
102 }
103
104 int num_pages = poppler_document_get_n_pages( print_data->doc );
105 if( num_pages <= 0 )
106 {
107 gtk_print_operation_cancel( operation );
108 return;
109 }
110
111 gtk_print_operation_set_n_pages( operation, num_pages );
112}
113
114void request_page_setup_callback( GtkPrintOperation* operation, GtkPrintContext* context, gint page_nr,
115 GtkPageSetup* setup, gpointer user_data )
116{
117 PrintData* print_data = static_cast<PrintData*>( user_data );
118
119 if( !print_data || !print_data->doc )
120 return;
121
122 PopplerPage* page = poppler_document_get_page( print_data->doc, page_nr );
123
124 if( !page )
125 return;
126
127 // Get page dimensions to determine orientation
128 double page_width, page_height;
129 poppler_page_get_size( page, &page_width, &page_height );
130
131 // Set orientation based on page dimensions
132 GtkPageOrientation orientation =
133 ( page_width > page_height ) ? GTK_PAGE_ORIENTATION_LANDSCAPE : GTK_PAGE_ORIENTATION_PORTRAIT;
134 gtk_page_setup_set_orientation( setup, orientation );
135
136 g_object_unref( page );
137}
138} // namespace
139
140namespace KIPLATFORM
141{
142namespace PRINTING
143{
144 PRINT_RESULT PrintPDF( const std::string& aFile, bool fit_to_page )
145 {
146 // Check file accessibility
147 if( access( aFile.c_str(), R_OK ) != 0 )
149
150 // Create file URI
151 gchar* uri = g_filename_to_uri( aFile.c_str(), NULL, NULL );
152 if( !uri )
154
155 // Load the PDF document
156 GError* error = NULL;
157 PopplerDocument* doc = poppler_document_new_from_file( uri, NULL, &error );
158 g_free( uri );
159
160 if( error )
161 {
162 g_error_free( error );
164 }
165
166 if( !doc )
168
169 auto cleanup_doc = std::unique_ptr<PopplerDocument, decltype(&g_object_unref)>(doc, &g_object_unref);
170
171 // Check if document has pages
172 int num_pages = poppler_document_get_n_pages( doc );
173
174 if( num_pages <= 0 )
176
177 // Create print data
178 PrintData print_data( doc );
179 print_data.fit_to_page = fit_to_page;
180
181 // Create print operation
182 GtkPrintOperation* op = gtk_print_operation_new();
183
184 if( !op )
186
187 auto cleanup_op = std::unique_ptr<GtkPrintOperation, decltype( &g_object_unref )>( op, &g_object_unref );
188
189 // Set up print operation properties
190 gtk_print_operation_set_use_full_page( op, FALSE );
191 gtk_print_operation_set_unit( op, GTK_UNIT_POINTS );
192
193 // Connect callbacks
194 g_signal_connect( op, "begin-print", G_CALLBACK( begin_print_callback ), &print_data );
195 g_signal_connect( op, "draw-page", G_CALLBACK( draw_page ), &print_data );
196 g_signal_connect( op, "request-page-setup", G_CALLBACK( request_page_setup_callback ), &print_data );
197
198 // Run the print operation
199 error = NULL;
200 GtkPrintOperationResult result =
201 gtk_print_operation_run( op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL, &error );
202
203 // Handle errors and determine result
204 PRINT_RESULT return_result;
205
206 if( error )
207 {
208 g_error_free( error );
209 return_result = PRINT_RESULT::FAILED_TO_PRINT;
210 }
211 else
212 {
213 switch( result )
214 {
215 case GTK_PRINT_OPERATION_RESULT_APPLY: return_result = PRINT_RESULT::OK; break;
216 case GTK_PRINT_OPERATION_RESULT_CANCEL: return_result = PRINT_RESULT::CANCELLED; break;
217 case GTK_PRINT_OPERATION_RESULT_ERROR:
218 default: return_result = PRINT_RESULT::FAILED_TO_PRINT; break;
219 }
220 }
221
222 return return_result;
223 }
224
225 PRINT_RESULT PrintPDF( const std::string& aFile )
226 {
227 return PrintPDF( aFile, true );
228 }
229
230} // namespace PRINTING
231} // namespace KIPLATFORM
PRINT_RESULT PrintPDF(const std::string &aFile)
const int scale
wxString result
Test unit parsing edge cases and error handling.