342 lines
11 KiB
C++
342 lines
11 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2009 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
|
|
* 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
|
|
*/
|
|
|
|
/**
|
|
* printout_controler.cpp
|
|
*/
|
|
|
|
// Set this to 1 if you want to test PostScript printing under MSW.
|
|
#define wxTEST_POSTSCRIPT_IN_MSW 1
|
|
|
|
#include <fctsys.h>
|
|
#include <appl_wxstruct.h>
|
|
#include <gr_basic.h>
|
|
#include <wxBasePcbFrame.h>
|
|
#include <class_drawpanel.h>
|
|
#include <confirm.h>
|
|
#include <base_units.h>
|
|
|
|
#include <class_board.h>
|
|
#include <pcbnew.h>
|
|
#include <protos.h>
|
|
|
|
#include <printout_controler.h>
|
|
|
|
// This class is an helper to pass print parameters to print functions
|
|
PRINT_PARAMETERS::PRINT_PARAMETERS()
|
|
{
|
|
m_PenDefaultSize = 50; // A reasonnable minimal value to draw items
|
|
// mainly that do not have a specifed line width
|
|
m_PrintScale = 1.0;
|
|
m_XScaleAdjust = m_YScaleAdjust = 1.0;
|
|
m_Print_Sheet_Ref = false;
|
|
m_PrintMaskLayer = 0xFFFFFFFF;
|
|
m_PrintMirror = false;
|
|
m_Print_Black_and_White = true;
|
|
m_OptionPrintPage = 1;
|
|
m_PageCount = 1;
|
|
m_ForceCentered = false;
|
|
m_Flags = 0;
|
|
m_DrillShapeOpt = PRINT_PARAMETERS::SMALL_DRILL_SHAPE;
|
|
m_PageSetupData = NULL;
|
|
}
|
|
|
|
|
|
BOARD_PRINTOUT_CONTROLER::BOARD_PRINTOUT_CONTROLER( const PRINT_PARAMETERS& print_params,
|
|
EDA_DRAW_FRAME* parent,
|
|
const wxString& title ) :
|
|
wxPrintout( title )
|
|
{
|
|
m_PrintParams = print_params; // Make a local copy of parameters.
|
|
// So they can change in printout controler
|
|
m_Parent = parent;
|
|
}
|
|
|
|
|
|
bool BOARD_PRINTOUT_CONTROLER::OnPrintPage( int page )
|
|
{
|
|
int layers_count = NB_LAYERS;
|
|
|
|
if( m_Parent->IsType( GERBER_FRAME ) )
|
|
layers_count = 32;
|
|
|
|
int mask_layer = m_PrintParams.m_PrintMaskLayer;
|
|
|
|
// compute layer mask from page number if we want one page per layer
|
|
if( m_PrintParams.m_OptionPrintPage == 0 ) // One page per layer
|
|
{
|
|
int ii, jj, mask = 1;
|
|
|
|
for( ii = 0, jj = 0; ii < layers_count; ii++ )
|
|
{
|
|
if( mask_layer & mask )
|
|
jj++;
|
|
|
|
if( jj == page )
|
|
{
|
|
m_PrintParams.m_PrintMaskLayer = mask;
|
|
break;
|
|
}
|
|
|
|
mask <<= 1;
|
|
}
|
|
}
|
|
|
|
if( m_PrintParams.m_PrintMaskLayer == 0 )
|
|
return false;
|
|
|
|
// In Pcbnew we can want the layer EDGE always printed
|
|
if( m_PrintParams.m_Flags == 1 )
|
|
m_PrintParams.m_PrintMaskLayer |= EDGE_LAYER;
|
|
|
|
DrawPage();
|
|
|
|
m_PrintParams.m_PrintMaskLayer = mask_layer;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*********************************************************/
|
|
void BOARD_PRINTOUT_CONTROLER::GetPageInfo( int* minPage, int* maxPage,
|
|
int* selPageFrom, int* selPageTo )
|
|
/*********************************************************/
|
|
{
|
|
*minPage = 1;
|
|
*selPageFrom = 1;
|
|
|
|
int icnt = 1;
|
|
if( m_PrintParams.m_OptionPrintPage == 0 )
|
|
icnt = m_PrintParams.m_PageCount;
|
|
|
|
*maxPage = icnt;
|
|
*selPageTo = icnt;
|
|
}
|
|
|
|
|
|
/*
|
|
* This is the real print function: print the active screen
|
|
*/
|
|
void BOARD_PRINTOUT_CONTROLER::DrawPage()
|
|
{
|
|
int tmpzoom;
|
|
wxPoint tmp_startvisu;
|
|
wxPoint old_org;
|
|
wxPoint DrawOffset; // Offset de trace
|
|
double userscale;
|
|
double DrawZoom = 1;
|
|
wxDC* dc = GetDC();
|
|
PCB_SCREEN* screen = (PCB_SCREEN*) m_Parent->GetScreen();
|
|
bool printMirror = m_PrintParams.m_PrintMirror;
|
|
|
|
wxBusyCursor dummy;
|
|
|
|
// Save old draw scale and draw offset
|
|
tmp_startvisu = screen->m_StartVisu;
|
|
tmpzoom = screen->GetZoom();
|
|
old_org = screen->m_DrawOrg;
|
|
|
|
// Change draw scale and offset to draw the whole page
|
|
screen->SetScalingFactor( DrawZoom );
|
|
screen->m_DrawOrg.x = screen->m_DrawOrg.y = 0;
|
|
screen->m_StartVisu.x = screen->m_StartVisu.y = 0;
|
|
|
|
PCB_BASE_FRAME* pcbframe = (PCB_BASE_FRAME*) m_Parent;
|
|
|
|
wxSize pageSizeIU = pcbframe->GetPageSizeIU(); // internal units
|
|
EDA_RECT bbbox = pcbframe->GetBoard()->ComputeBoundingBox();
|
|
|
|
// 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:
|
|
if( m_Parent->IsType( MODULE_EDITOR_FRAME ) )
|
|
{
|
|
bbbox.Move( wxPoint( pageSizeIU.x/2, pageSizeIU.y/2 ) );
|
|
}
|
|
|
|
// Compute the PCB size in internal units
|
|
userscale = m_PrintParams.m_PrintScale;
|
|
|
|
if( userscale == 0 ) // fit in page
|
|
{
|
|
// Margin = 10mm
|
|
int extra_margin = int( 10 * IU_PER_MM ); // deci-mils
|
|
|
|
pageSizeIU.x = bbbox.GetWidth() + extra_margin * 2;
|
|
pageSizeIU.y = bbbox.GetHeight() + extra_margin * 2;
|
|
|
|
userscale = 0.99;
|
|
}
|
|
|
|
if( (m_PrintParams.m_PrintScale > 1.0) // scale > 1 -> Recadrage
|
|
|| (m_PrintParams.m_PrintScale == 0) ) // fit in page
|
|
{
|
|
DrawOffset += bbbox.Centre();
|
|
}
|
|
|
|
if( m_PrintParams.m_PageSetupData )
|
|
{
|
|
wxSize pagesize;
|
|
|
|
pagesize.x = int( pageSizeIU.x / userscale );
|
|
pagesize.y = int( pageSizeIU.y / userscale );
|
|
|
|
FitThisSizeToPageMargins( pagesize, *m_PrintParams.m_PageSetupData );
|
|
}
|
|
|
|
// Compute Accurate scale 1
|
|
if( userscale == 1.0 )
|
|
{
|
|
// We want a 1:1 scale and margins for printing
|
|
MapScreenSizeToPaper();
|
|
int w, h;
|
|
GetPPIPrinter( &w, &h );
|
|
double accurate_Xscale = ( (double) ( DrawZoom * w ) ) / (IU_PER_MILS*1000);
|
|
double accurate_Yscale = ( (double) ( DrawZoom * h ) ) / (IU_PER_MILS*1000);
|
|
|
|
if( IsPreview() ) // Scale must take in account the DC size in Preview
|
|
{
|
|
// Get the size of the DC in pixels
|
|
wxSize PlotAreaSize;
|
|
dc->GetSize( &PlotAreaSize.x, &PlotAreaSize.y );
|
|
GetPageSizePixels( &w, &h );
|
|
accurate_Xscale *= PlotAreaSize.x;
|
|
accurate_Xscale /= (double) w;
|
|
accurate_Yscale *= PlotAreaSize.y;
|
|
accurate_Yscale /= (double) h;
|
|
}
|
|
accurate_Xscale *= m_PrintParams.m_XScaleAdjust;
|
|
accurate_Yscale *= m_PrintParams.m_YScaleAdjust;
|
|
// Fine scale adjust
|
|
dc->SetUserScale( accurate_Xscale, accurate_Yscale );
|
|
}
|
|
|
|
// Get the final size of the DC in pixels
|
|
wxSize PlotAreaSizeInPixels;
|
|
dc->GetSize( &PlotAreaSizeInPixels.x, &PlotAreaSizeInPixels.y );
|
|
double scalex, scaley;
|
|
dc->GetUserScale(&scalex, &scaley);
|
|
wxSize PlotAreaSizeInUserUnits;
|
|
PlotAreaSizeInUserUnits.x = (int) (PlotAreaSizeInPixels.x/scalex);
|
|
PlotAreaSizeInUserUnits.y = (int) (PlotAreaSizeInPixels.y/scaley);
|
|
|
|
/* In some cases the plot origin is the centre of the page
|
|
* when:
|
|
* - Asked
|
|
* - scale > 1
|
|
* - fit in page
|
|
*/
|
|
if( m_PrintParams.m_ForceCentered
|
|
|| (m_PrintParams.m_PrintScale > 1.0) // scale > 1
|
|
|| (m_PrintParams.m_PrintScale == 0) ) // fit in page
|
|
{
|
|
DrawOffset.x -= PlotAreaSizeInUserUnits.x / 2;
|
|
DrawOffset.y -= PlotAreaSizeInUserUnits.y / 2;
|
|
}
|
|
|
|
screen->m_DrawOrg = DrawOffset;
|
|
|
|
GRResetPenAndBrush( dc );
|
|
|
|
if( m_PrintParams.m_Print_Black_and_White )
|
|
GRForceBlackPen( true );
|
|
|
|
EDA_DRAW_PANEL* panel = m_Parent->GetCanvas();
|
|
EDA_RECT tmp = *panel->GetClipBox();
|
|
|
|
// Set clip box to the max size
|
|
#define MAX_VALUE (INT_MAX/2) // MAX_VALUE is the max we can use in an integer
|
|
// and that allows calculations without overflow
|
|
panel->SetClipBox( EDA_RECT( wxPoint( 0, 0 ), wxSize( MAX_VALUE, MAX_VALUE ) ) );
|
|
|
|
m_Parent->GetScreen()->m_IsPrinting = true;
|
|
int bg_color = g_DrawBgColor;
|
|
|
|
if( m_PrintParams.m_Print_Sheet_Ref )
|
|
m_Parent->TraceWorkSheet( dc, screen, m_PrintParams.m_PenDefaultSize,
|
|
IU_PER_MILS );
|
|
|
|
if( printMirror )
|
|
{
|
|
// To plot mirror, we reverse the y axis, and modify the plot y origin
|
|
dc->SetAxisOrientation( true, true );
|
|
|
|
if( userscale < 1.0 )
|
|
scaley /= userscale;
|
|
|
|
/* Plot offset y is moved by the y plot area size in order to have
|
|
* the old draw area in the new draw area, because the draw origin has not moved
|
|
* (this is the upper left corner) but the Y axis is reversed, therefore the plotting area
|
|
* is the y coordinate values from - PlotAreaSize.y to 0 */
|
|
int y_dc_offset = PlotAreaSizeInPixels.y;
|
|
y_dc_offset = (int) ( ( double ) y_dc_offset * userscale );
|
|
dc->SetDeviceOrigin( 0, y_dc_offset );
|
|
int ysize = (int) ( PlotAreaSizeInPixels.y / scaley );
|
|
DrawOffset.y += ysize;
|
|
|
|
/* in order to keep the board position in the sheet
|
|
* (when user scale <= 1) the y offset in moved by the distance between
|
|
* the middle of the page and the middle of the board
|
|
* This is equivalent to put the mirror axis to the board centre
|
|
* for scales > 1, the DrawOffset was already computed to have the board centre
|
|
* to the middle of the page.
|
|
*/
|
|
wxPoint pcb_centre = bbbox.Centre();
|
|
|
|
if( userscale <= 1.0 )
|
|
DrawOffset.y += pcb_centre.y - (ysize / 2);
|
|
|
|
dc->SetLogicalOrigin( screen->m_DrawOrg.x, screen->m_DrawOrg.y );
|
|
panel->SetClipBox( EDA_RECT( wxPoint( -MAX_VALUE/2, -MAX_VALUE/2 ),
|
|
panel->GetClipBox()->GetSize() ) );
|
|
}
|
|
|
|
g_DrawBgColor = WHITE;
|
|
|
|
/* 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:
|
|
* In order to plot on a white background in OR mode we must:
|
|
* 1 - Plot all items in black, this creates a local black background
|
|
* 2 - Plot in OR mode on black "local" background
|
|
*/
|
|
if( !m_PrintParams.m_Print_Black_and_White )
|
|
{ // Creates a "local" black background
|
|
GRForceBlackPen( true );
|
|
m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer, printMirror, &m_PrintParams );
|
|
GRForceBlackPen( false );
|
|
}
|
|
|
|
m_Parent->PrintPage( dc, m_PrintParams.m_PrintMaskLayer, printMirror, &m_PrintParams );
|
|
|
|
g_DrawBgColor = bg_color;
|
|
m_Parent->GetScreen()->m_IsPrinting = false;
|
|
panel->SetClipBox( tmp );
|
|
|
|
GRForceBlackPen( false );
|
|
|
|
screen->m_StartVisu = tmp_startvisu;
|
|
screen->m_DrawOrg = old_org;
|
|
screen->SetZoom( tmpzoom );
|
|
}
|