printout_controler.cpp 13 KB
Newer Older
1 2 3
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
4
 * Copyright (C) 2009 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors.
 *
 * 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, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

/**
26 27
 * @file printout_controler.cpp
 * @brief Board print handler implementation file.
28
 */
29

30

31 32 33
// Set this to 1 if you want to test PostScript printing under MSW.
#define wxTEST_POSTSCRIPT_IN_MSW 1

34
#include <fctsys.h>
35
#include <pgm_base.h>
36 37 38
#include <gr_basic.h>
#include <class_drawpanel.h>
#include <confirm.h>
39
#include <base_units.h>
40 41 42 43
#include <wxBasePcbFrame.h>
#include <class_board.h>
#include <pcbnew.h>

44
#include <printout_controler.h>
45

46 47 48 49 50 51 52 53

/**
 * Definition for enabling and disabling print controller trace output.  See the
 * wxWidgets documentation on using the WXTRACE environment variable.
 */
static const wxString tracePrinting( wxT( "KicadPrinting" ) );


54 55
PRINT_PARAMETERS::PRINT_PARAMETERS()
{
56
    m_PenDefaultSize        = Millimeter2iu( 0.2 ); // A reasonable default value to draw items
57
                                      // which do not have a specified line width
58 59 60 61
    m_PrintScale            = 1.0;
    m_XScaleAdjust          = 1.0;
    m_YScaleAdjust          = 1.0;
    m_Print_Sheet_Ref       = false;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
62
    m_PrintMaskLayer.set();
63
    m_PrintMirror           = false;
64
    m_Print_Black_and_White = true;
65 66 67 68 69 70
    m_OptionPrintPage       = 1;
    m_PageCount             = 1;
    m_ForceCentered         = false;
    m_Flags                 = 0;
    m_DrillShapeOpt         = PRINT_PARAMETERS::SMALL_DRILL_SHAPE;
    m_PageSetupData         = NULL;
71 72 73
}


74 75 76 77
BOARD_PRINTOUT_CONTROLLER::BOARD_PRINTOUT_CONTROLLER( const PRINT_PARAMETERS& aParams,
                                                      EDA_DRAW_FRAME*         aParent,
                                                      const wxString&         aTitle ) :
    wxPrintout( aTitle )
78
{
79 80
    m_PrintParams = aParams;   // Make a local copy of the print parameters.
    m_Parent = aParent;
81 82 83
}


84
bool BOARD_PRINTOUT_CONTROLLER::OnPrintPage( int aPage )
85
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
86
    LSET lset = m_PrintParams.m_PrintMaskLayer;
87 88

    // compute layer mask from page number if we want one page per layer
89
    if( m_PrintParams.m_OptionPrintPage == 0 )  // One page per layer
90
    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
91 92 93 94 95
        // This sequence is TBD, call a different
        // sequencer if needed, such as Seq().  Could not find documentation on
        // page order.
        LSEQ seq = lset.UIOrder();

96 97 98
        // aPage starts at 1, not 0
        if( unsigned( aPage-1 ) < seq.size() )
            m_PrintParams.m_PrintMaskLayer = LSET( seq[aPage-1] );
99 100
    }

Dick Hollenbeck's avatar
Dick Hollenbeck committed
101
    if( !m_PrintParams.m_PrintMaskLayer.any() )
102 103
        return false;

104
    // In Pcbnew we can want the layer EDGE always printed
105
    if( m_PrintParams.m_Flags == 1 )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
106
        m_PrintParams.m_PrintMaskLayer.set( Edge_Cuts );
107 108 109

    DrawPage();

Dick Hollenbeck's avatar
Dick Hollenbeck committed
110
    m_PrintParams.m_PrintMaskLayer = lset;
111 112 113 114 115

    return true;
}


116 117
void BOARD_PRINTOUT_CONTROLLER::GetPageInfo( int* minPage, int* maxPage,
                                             int* selPageFrom, int* selPageTo )
118 119 120 121 122
{
    *minPage     = 1;
    *selPageFrom = 1;

    int icnt = 1;
123

124 125 126 127 128 129 130 131
    if( m_PrintParams.m_OptionPrintPage == 0 )
        icnt = m_PrintParams.m_PageCount;

    *maxPage   = icnt;
    *selPageTo = icnt;
}


132
void BOARD_PRINTOUT_CONTROLLER::DrawPage()
133
{
134 135 136 137 138
    wxPoint       offset;
    double        userscale;
    EDA_RECT      boardBoundingBox;
    EDA_RECT      drawRect;
    wxDC*         dc = GetDC();
139
    BASE_SCREEN*  screen = m_Parent->GetScreen();
140
    bool          printMirror = m_PrintParams.m_PrintMirror;
141
    wxSize        pageSizeIU = m_Parent->GetPageSizeIU();
142

143
    wxBusyCursor  dummy;
144

145 146 147
    BOARD * brd = ((PCB_BASE_FRAME*) m_Parent)->GetBoard();
    boardBoundingBox = brd->ComputeBoundingBox();
    wxString titleblockFilename = brd->GetFileName();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
148

149 150 151
    // Use the page size as the drawing area when the board is shown or the user scale
    // is less than 1.
    if( m_PrintParams.PrintBorderAndTitleBlock() )
152
        boardBoundingBox = EDA_RECT( wxPoint( 0, 0 ), pageSizeIU );
153

154
    wxLogTrace( tracePrinting, wxT( "Drawing bounding box:                 x=%d, y=%d, w=%d, h=%d" ),
155 156 157
                boardBoundingBox.GetX(), boardBoundingBox.GetY(),
                boardBoundingBox.GetWidth(), boardBoundingBox.GetHeight() );

158
    // Compute the PCB size in internal units
159
    userscale = m_PrintParams.m_PrintScale;
160

161
    if( m_PrintParams.m_PrintScale == 0 )   //  fit in page option
162
    {
163 164 165 166 167 168 169 170 171 172 173
        if(boardBoundingBox.GetWidth() && boardBoundingBox.GetHeight())
        {
            int margin = Millimeter2iu( 10.0 ); // add a margin around the drawings
            double scaleX = (double)(pageSizeIU.x - (2 * margin)) /
                            boardBoundingBox.GetWidth();
            double scaleY = (double)(pageSizeIU.y - (2 * margin)) /
                            boardBoundingBox.GetHeight();
            userscale = (scaleX < scaleY) ? scaleX : scaleY;
        }
        else
            userscale = 1.0;
174 175
    }

176
    wxSize scaledPageSize = pageSizeIU;
177
    drawRect.SetSize( scaledPageSize );
178 179
    scaledPageSize.x = wxRound( scaledPageSize.x / userscale );
    scaledPageSize.y = wxRound( scaledPageSize.y / userscale );
180

181

182 183
    if( m_PrintParams.m_PageSetupData )
    {
184 185
        wxLogTrace( tracePrinting, wxT( "Fit size to page margins:         x=%d, y=%d" ),
                    scaledPageSize.x, scaledPageSize.y );
186

187 188
        // Always scale to the size of the paper.
        FitThisSizeToPageMargins( scaledPageSize, *m_PrintParams.m_PageSetupData );
189
    }
190 191

    // Compute Accurate scale 1
192
    if( m_PrintParams.m_PrintScale == 1.0 )
193
    {
194 195 196
        // We want a 1:1 scale, regardless the page setup
        // like page size, margin ...
        MapScreenSizeToPaper(); // set best scale and offset (scale is not used)
197 198
        int w, h;
        GetPPIPrinter( &w, &h );
199 200
        double accurate_Xscale = (double) w / (IU_PER_MILS*1000);
        double accurate_Yscale = (double) h / (IU_PER_MILS*1000);
201

202 203 204
        if( IsPreview() )  // Scale must take in account the DC size in Preview
        {
            // Get the size of the DC in pixels
205
            wxSize       PlotAreaSize;
206 207
            dc->GetSize( &PlotAreaSize.x, &PlotAreaSize.y );
            GetPageSizePixels( &w, &h );
208 209
            accurate_Xscale *= (double)PlotAreaSize.x / w;
            accurate_Yscale *= (double)PlotAreaSize.y / h;
210
        }
211
        // Fine scale adjust
212 213
        accurate_Xscale *= m_PrintParams.m_XScaleAdjust;
        accurate_Yscale *= m_PrintParams.m_YScaleAdjust;
214

215
        // Set print scale for 1:1 exact scale
216
        dc->SetUserScale( accurate_Xscale, accurate_Yscale );
217 218
    }

219 220 221
    // Get the final size of the DC in pixels
    wxSize       PlotAreaSizeInPixels;
    dc->GetSize( &PlotAreaSizeInPixels.x, &PlotAreaSizeInPixels.y );
222 223
    wxLogTrace( tracePrinting, wxT( "Plot area in pixels:              x=%d, y=%d" ),
                PlotAreaSizeInPixels.x, PlotAreaSizeInPixels.y );
224
    double scalex, scaley;
225 226 227 228
    dc->GetUserScale( &scalex, &scaley );
    wxLogTrace( tracePrinting, wxT( "DC user scale:                    x=%g, y=%g" ),
                scalex, scaley );

229
    wxSize PlotAreaSizeInUserUnits;
230 231
    PlotAreaSizeInUserUnits.x = KiROUND( PlotAreaSizeInPixels.x / scalex );
    PlotAreaSizeInUserUnits.y = KiROUND( PlotAreaSizeInPixels.y / scaley );
232 233
    wxLogTrace( tracePrinting, wxT( "Scaled plot area in user units:   x=%d, y=%d" ),
                PlotAreaSizeInUserUnits.x, PlotAreaSizeInUserUnits.y );
234

235 236 237
    // In module editor, the module is located at 0,0 but for printing
    // it is moved to pageSizeIU.x/2, pageSizeIU.y/2.
    // So the equivalent board must be moved to the center of the page:
238
    if( m_Parent->IsType( FRAME_PCB_MODULE_EDITOR ) )
239 240 241 242
    {
        boardBoundingBox.Move( wxPoint( pageSizeIU.x/2, pageSizeIU.y/2 ) );
    }

243 244 245
    // In some cases the plot origin is the centre of the board outline rather than the center
    // of the selected paper size.
    if( m_PrintParams.CenterOnBoardOutline() )
246
    {
247 248 249 250 251 252 253 254 255 256
        // Here we are only drawing the board and it's contents.
        drawRect = boardBoundingBox;
        offset.x += wxRound( (double) -scaledPageSize.x / 2.0 );
        offset.y += wxRound( (double) -scaledPageSize.y / 2.0 );

        wxPoint center = boardBoundingBox.Centre();

        if( printMirror )
        {
            // Calculate the mirrored center of the board.
257
            center.x = m_Parent->GetPageSizeIU().x - boardBoundingBox.Centre().x;
258
        }
259

260 261
        offset += center;
    }
262 263

    GRResetPenAndBrush( dc );
264

265
    EDA_DRAW_PANEL* panel = m_Parent->GetCanvas();
266
    EDA_RECT        tmp   = *panel->GetClipBox();
267

268
    // Set clip box to the max size
269 270
    #define MAX_VALUE (INT_MAX/2)   // MAX_VALUE is the max we can use in an integer
                                    // and that allows calculations without overflow
271
    panel->SetClipBox( EDA_RECT( wxPoint( 0, 0 ), wxSize( MAX_VALUE, MAX_VALUE ) ) );
272

273
    screen->m_IsPrinting = true;
274
    EDA_COLOR_T bg_color = m_Parent->GetDrawBgColor();
275

276
    // Print frame reference, if requested, before
277 278 279 280 281 282 283
    if( m_PrintParams.m_Print_Black_and_White )
        GRForceBlackPen( true );

    if( m_PrintParams.PrintBorderAndTitleBlock() )
        m_Parent->DrawWorkSheet( dc, screen, m_PrintParams.m_PenDefaultSize,
                                  IU_PER_MILS, titleblockFilename );

284 285
    if( printMirror )
    {
286 287
        // To plot mirror, we reverse the x axis, and modify the plot x origin
        dc->SetAxisOrientation( false, false);
288

289
        /* Plot offset x is moved by the x plot area size in order to have
290
         * the old draw area in the new draw area, because the draw origin has not moved
291 292 293 294 295
         * (this is the upper left corner) but the X axis is reversed, therefore the plotting area
         * is the x coordinate values from  - PlotAreaSize.x to 0 */
        int x_dc_offset = PlotAreaSizeInPixels.x;
        x_dc_offset = KiROUND( x_dc_offset  * userscale );
        dc->SetDeviceOrigin( x_dc_offset, 0 );
296 297

        wxLogTrace( tracePrinting, wxT( "Device origin:                    x=%d, y=%d" ),
298
                    x_dc_offset, 0 );
299

300 301
        panel->SetClipBox( EDA_RECT( wxPoint( -MAX_VALUE/2, -MAX_VALUE/2 ),
                                     panel->GetClipBox()->GetSize() ) );
302 303
    }

304 305 306 307 308 309
    // screen->m_DrawOrg = offset;
    dc->SetLogicalOrigin( offset.x, offset.y );

    wxLogTrace( tracePrinting, wxT( "Logical origin:                   x=%d, y=%d" ),
                offset.x, offset.y );

310
#if defined(wxUSE_LOG_TRACE) && defined( DEBUG )
311
    wxRect paperRect = GetPaperRectPixels();
312 313
    wxLogTrace( tracePrinting, wxT( "Paper rectangle:                  left=%d, top=%d, "
                                    "right=%d, bottom=%d" ),
314 315 316 317 318 319 320
                paperRect.GetLeft(), paperRect.GetTop(), paperRect.GetRight(),
                paperRect.GetBottom() );

    int devLeft = dc->LogicalToDeviceX( drawRect.GetX() );
    int devTop = dc->LogicalToDeviceY( drawRect.GetY() );
    int devRight = dc->LogicalToDeviceX( drawRect.GetRight() );
    int devBottom = dc->LogicalToDeviceY( drawRect.GetBottom() );
321 322
    wxLogTrace( tracePrinting, wxT( "Final device rectangle:           left=%d, top=%d, "
                                    "right=%d, bottom=%d\n" ),
323
                devLeft, devTop, devRight, devBottom );
324
#endif
325

326
    m_Parent->SetDrawBgColor( WHITE );
327

328 329
    /* when printing in color mode, we use the graphic OR mode that gives the same look as
     * the screen but because the background is white when printing, we must use a trick:
330
     * In order to plot on a white background in OR mode we must:
331
     * 1 - Plot all items in black, this creates a local black background
332 333 334
     * 2 - Plot in OR mode on black "local" background
     */
    if( !m_PrintParams.m_Print_Black_and_White )
335 336
    {
        // Creates a "local" black background
337
        GRForceBlackPen( true );
338 339
        m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer,
                             printMirror, &m_PrintParams );
340 341
        GRForceBlackPen( false );
    }
342
    else
343 344
        GRForceBlackPen( true );

345

346 347
    m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer, printMirror,
                         &m_PrintParams );
348

349
    m_Parent->SetDrawBgColor( bg_color );
350
    screen->m_IsPrinting = false;
351
    panel->SetClipBox( tmp );
352 353
    GRForceBlackPen( false );
}