/** * @file pcbnew/pcbplot.cpp */ /* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 1992-2012 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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include /** Get the 'traditional' gerber extension depending on the layer */ static wxString GetGerberExtension( int layer )/*{{{*/ { switch( layer ) { case LAYER_N_FRONT: return wxString( wxT( "gtl" ) ); case LAYER_N_2: case LAYER_N_3: case LAYER_N_4: case LAYER_N_5: case LAYER_N_6: case LAYER_N_7: case LAYER_N_8: case LAYER_N_9: case LAYER_N_10: case LAYER_N_11: case LAYER_N_12: case LAYER_N_13: case LAYER_N_14: case LAYER_N_15: // TODO: see if we use .gbr or a layer identifier (gb1 .. gbnn ?) // according to the new internal layers designation // (1 is the first internal layer from the front layer) return wxString( wxT( "gbr" ) ); case LAYER_N_BACK: return wxString( wxT( "gbl" ) ); case ADHESIVE_N_BACK: return wxString( wxT( "gba" ) ); case ADHESIVE_N_FRONT: return wxString( wxT( "gta" ) ); case SOLDERPASTE_N_BACK: return wxString( wxT( "gbp" ) ); case SOLDERPASTE_N_FRONT: return wxString( wxT( "gtp" ) ); case SILKSCREEN_N_BACK: return wxString( wxT( "gbo" ) ); case SILKSCREEN_N_FRONT: return wxString( wxT( "gto" ) ); case SOLDERMASK_N_BACK: return wxString( wxT( "gbs" ) ); case SOLDERMASK_N_FRONT: return wxString( wxT( "gts" ) ); case DRAW_N: case COMMENT_N: case ECO1_N: case ECO2_N: case EDGE_N: default: return wxString( wxT( "gbr" ) ); } }/*}}}*/ /** Complete a plot filename: forces the output directory, add a suffix to the name and sets the extension if specified */ static void BuildPlotFileName( wxFileName *aFilename, /*{{{*/ const wxString& aOutputDir, wxString aSuffix, const wxString& aExtension ) { aFilename->SetPath( aOutputDir ); // Set the file extension aFilename->SetExt( aExtension ); /* remove leading and trailing spaces if any from the suffix, if something survives add it to the name; also the suffix can contain an extension: if that's the case, apply it */ aSuffix.Trim( true ); aSuffix.Trim( false ); wxFileName suffix_fn( aSuffix ); if( suffix_fn.HasName() ) aFilename->SetName( aFilename->GetName() + wxT( "-" ) + suffix_fn.GetName() ); if( suffix_fn.HasExt() ) aFilename->SetExt( suffix_fn.GetExt() ); }/*}}}*/ /** Fix the output directory pathname to absolute and ensure it exists */ static bool EnsureOutputDirectory( wxFileName *aOutputDir, /*{{{*/ const wxString& aBoardFilename, wxTextCtrl* aMessageBox ) { wxString boardFilePath = wxFileName( aBoardFilename ).GetPath(); if( !aOutputDir->MakeAbsolute( boardFilePath ) ) { wxString msg; msg.Printf( _( "Cannot make %s absolute with respect to %s!" ), GetChars( aOutputDir->GetPath() ), GetChars( boardFilePath ) ); wxMessageBox( msg, _( "Plot" ), wxOK | wxICON_ERROR ); return false; } wxString outputPath( aOutputDir->GetPath() ); if( !wxFileName::DirExists( outputPath ) ) { if( wxMkdir( outputPath ) ) { if( aMessageBox ) { wxString msg; msg.Printf( _( "Directory %s created.\n" ), GetChars( outputPath ) ); aMessageBox->AppendText( msg ); return true; } } else { wxMessageBox( _( "Cannot create output directory!" ), _( "Plot" ), wxOK | wxICON_ERROR ); return false; } } return true; }/*}}}*/ /* * DIALOG_PLOT:Plot * Actually creates the files */ void DIALOG_PLOT::Plot( wxCommandEvent& event ) { int layer; applyPlotSettings(); // Create output directory if it does not exist (also transform it in // absolute form). Bail if it fails wxFileName outputDir = wxFileName::DirName( m_plotOpts.GetOutputDirectory() ); wxString boardFilename = m_parent->GetBoard()->GetFileName(); if( !EnsureOutputDirectory( &outputDir, boardFilename, m_messagesBox ) ) return; m_plotOpts.SetAutoScale( false ); m_plotOpts.SetScale( 1 ); switch( m_plotOpts.GetScaleSelection() ) { default: break; case 0: // Autoscale option m_plotOpts.SetAutoScale( true ); break; case 2: // 3:2 option m_plotOpts.SetScale( 1.5 ); break; case 3: // 2:1 option m_plotOpts.SetScale( 2 ); break; case 4: // 3:1 option m_plotOpts.SetScale( 3 ); break; } /* If the scale factor edit controls are disabled or the scale value * is 0, don't adjust the base scale factor. This fixes a bug when * the default scale adjust is initialized to 0 and saved in program * settings resulting in a divide by zero fault. */ if( m_fineAdjustXscaleOpt->IsEnabled() && m_XScaleAdjust != 0.0 ) m_plotOpts.SetFineScaleAdjustX( m_XScaleAdjust ); if( m_fineAdjustYscaleOpt->IsEnabled() && m_YScaleAdjust != 0.0 ) m_plotOpts.SetFineScaleAdjustY( m_YScaleAdjust ); if( m_PSFineAdjustWidthOpt->IsEnabled() ) m_plotOpts.SetWidthAdjust( m_PSWidthAdjust ); wxString file_ext( GetDefaultPlotExtension( m_plotOpts.GetFormat() ) ); // Test for a reasonable scale value // XXX could this actually happen? isn't it constrained in the apply // function? if( m_plotOpts.GetScale() < PLOT_MIN_SCALE ) DisplayInfoMessage( this, _( "Warning: Scale option set to a very small value" ) ); if( m_plotOpts.GetScale() > PLOT_MAX_SCALE ) DisplayInfoMessage( this, _( "Warning: Scale option set to a very large value" ) ); // Save the current plot options in the board m_parent->SetPlotSettings( m_plotOpts ); long layerMask = 1; for( layer = 0; layer < NB_LAYERS; layer++, layerMask <<= 1 ) { if( m_plotOpts.GetLayerSelection() & layerMask ) { // Pick the basename from the board file wxFileName fn( boardFilename ); // Use Gerber Extensions based on layer number // (See http://en.wikipedia.org/wiki/Gerber_File) if( ( m_plotOpts.GetFormat() == PLOT_FORMAT_GERBER ) && m_useGerberExtensions->GetValue() ) file_ext = GetGerberExtension( layer ); // Create file name (from the English layer name for non copper layers). BuildPlotFileName( &fn, outputDir.GetPath(), m_board->GetLayerName( layer, false ), file_ext ); LOCALE_IO toggle; BOARD *board = m_parent->GetBoard(); PLOTTER *plotter = StartPlotBoard(board, &m_plotOpts, fn.GetFullPath(), wxEmptyString ); // Print diags in messages box: wxString msg; if( plotter ) { PlotOneBoardLayer( board, plotter, layer, m_plotOpts ); plotter->EndPlot(); delete plotter; msg.Printf( _( "Plot file <%s> created" ), GetChars( fn.GetFullPath() ) ); } else msg.Printf( _( "Unable to create <%s>" ), GetChars( fn.GetFullPath() ) ); msg << wxT( "\n" ); m_messagesBox->AppendText( msg ); } } // If no layer selected, we have nothing plotted. // Prompt user if it happens because he could think there is a bug in Pcbnew. if( !m_plotOpts.GetLayerSelection() ) DisplayError( this, _( "No layer selected" ) ); } void PCB_EDIT_FRAME::ToPlotter( wxCommandEvent& event ) { DIALOG_PLOT dlg( this ); dlg.ShowModal(); } /** Batch plotter constructor, nothing interesting here */ PLOT_CONTROLLER::PLOT_CONTROLLER( BOARD *aBoard ) : m_plotter( NULL ), m_board( aBoard ) { } /** Batch plotter destructor, ensures that the last plot is closed */ PLOT_CONTROLLER::~PLOT_CONTROLLER() { ClosePlot(); } /* IMPORTANT THING TO KNOW: the locale during plots *MUST* be kept as * C/POSIX using a LOCALE_IO object on the stack. This even when * opening/closing the plotfile, since some drivers do I/O even then */ /** Close the current plot, nothing happens if it isn't open */ void PLOT_CONTROLLER::ClosePlot() { LOCALE_IO toggle; if( m_plotter ) { m_plotter->EndPlot(); delete m_plotter; m_plotter = NULL; } } /** Open a new plotfile; works as a factory for plotter objects */ bool PLOT_CONTROLLER::OpenPlotfile( const wxString &aSuffix, /*{{{*/ PlotFormat aFormat, const wxString &aSheetDesc ) { LOCALE_IO toggle; /* Save the current format: sadly some plot routines depends on this but the main reason is that the StartPlot method uses it to dispatch the plotter creation */ m_plotOpts.SetFormat( aFormat ); // Ensure that the previous plot is closed ClosePlot(); // Now compute the full filename for the output and start the plot // (after ensuring the output directory is OK) wxString outputDirName = m_plotOpts.GetOutputDirectory() ; wxFileName outputDir = wxFileName::DirName( outputDirName ); wxString boardFilename = m_board->GetFileName(); if( EnsureOutputDirectory( &outputDir, boardFilename, NULL ) ) { wxFileName fn( boardFilename ); BuildPlotFileName( &fn, outputDirName, aSuffix, GetDefaultPlotExtension( aFormat ) ); m_plotter = StartPlotBoard( m_board, &m_plotOpts, fn.GetFullPath(), aSheetDesc ); } return( m_plotter != NULL ); }/*}}}*/ /** Plot a single layer on the current plotfile */ bool PLOT_CONTROLLER::PlotLayer( int aLayer )/*{{{*/ { LOCALE_IO toggle; // No plot open, nothing to do... if( !m_plotter ) return false; // Fully delegated to the parent PlotOneBoardLayer( m_board, m_plotter, aLayer, m_plotOpts ); return true; }/*}}}*/