KiCad PCB EDA Suite
dialog_export_svg.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 (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25
26#include <pcb_edit_frame.h>
27#include <pcbnew_settings.h>
29#include <reporter.h>
31#include <confirm.h>
32#include <core/arraydim.h>
33#include <core/kicad_algo.h>
34#include <pcbplot.h>
35#include <locale_io.h>
36#include <board.h>
38#include <bitmaps.h>
42#include <wx/dirdlg.h>
43#include <pgm_base.h>
44#include <pcb_plot_svg.h>
45
47{
48public:
49 DIALOG_EXPORT_SVG( PCB_EDIT_FRAME* aParent, BOARD* aBoard );
50 ~DIALOG_EXPORT_SVG() override;
51
52private:
56 // the list of existing board layers in wxCheckListBox, with the
57 // board layers id:
58 std::pair<wxCheckListBox*, int> m_boxSelectLayer[PCB_LAYER_ID_COUNT];
63
64 void initDialog();
65
66 void OnButtonPlot( wxCommandEvent& event ) override;
67
68 void onPagePerLayerClicked( wxCommandEvent& event ) override;
69 void OnOutputDirectoryBrowseClicked( wxCommandEvent& event ) override;
70 void ExportSVGFile( bool aOnlyOneFile );
71
73};
74
75
76/*
77 * DIALOG_EXPORT_SVG functions
78 */
80 DIALOG_EXPORT_SVG_BASE( aParent ),
81 m_board( aBoard ),
82 m_parent( aParent ),
83 m_printBW( false ),
84 m_printMirror( false ),
85 m_oneFileOnly( false )
86{
88
89 m_messagesPanel->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
90
91 initDialog();
92
93 SetupStandardButtons( { { wxID_OK, _( "Export" ) },
94 { wxID_CANCEL, _( "Close" ) } } );
95
97}
98
99
101{
102 m_printBW = m_ModeColorOption->GetSelection();
105 m_outputDirectory.Replace( wxT( "\\" ), wxT( "/" ) );
106
107 auto cfg = m_parent->GetPcbNewSettings();
108
110 cfg->m_ExportSvg.mirror = m_printMirror;
111 cfg->m_ExportSvg.one_file = m_oneFileOnly;
112 cfg->m_ExportSvg.page_size = m_rbSvgPageSizeOpt->GetSelection();
113 cfg->m_ExportSvg.output_dir = m_outputDirectory.ToStdString();
114
115 if( m_checkboxPagePerLayer->GetValue() )
116 {
117 m_oneFileOnly = false;
118 cfg->m_ExportSvg.plot_board_edges = m_checkboxEdgesOnAllPages->GetValue();
119 }
120 else
121 {
122 m_oneFileOnly = true;
123 }
124
125 cfg->m_ExportSvg.layers.clear();
126
127 for( unsigned layer = 0; layer < arrayDim( m_boxSelectLayer ); ++layer )
128 {
129 if( !m_boxSelectLayer[layer].first )
130 continue;
131
132 if( m_boxSelectLayer[layer].first->IsChecked( m_boxSelectLayer[layer].second ) )
133 cfg->m_ExportSvg.layers.push_back( layer );
134 }
135}
136
137
139{
141
146
147 m_rbSvgPageSizeOpt->SetSelection( cfg->m_ExportSvg.page_size );
149
150 wxCommandEvent dummy;
152
154
155 m_ModeColorOption->SetSelection( m_printBW ? 1 : 0 );
156 m_printMirrorOpt->SetValue( m_printMirror );
157
158 for( LSEQ seq = m_board->GetEnabledLayers().UIOrder(); seq; ++seq )
159 {
160 PCB_LAYER_ID layer = *seq;
161 int checkIndex;
162
163 if( IsCopperLayer( layer ) )
164 {
165 checkIndex = m_CopperLayersList->Append( m_board->GetLayerName( layer ) );
166 m_boxSelectLayer[layer] = std::make_pair( m_CopperLayersList, checkIndex );
167 }
168 else
169 {
170 checkIndex = m_TechnicalLayersList->Append( m_board->GetLayerName( layer ) );
171 m_boxSelectLayer[layer] = std::make_pair( m_TechnicalLayersList, checkIndex );
172 }
173
174 if( alg::contains( cfg->m_ExportSvg.layers, layer ) )
175 m_boxSelectLayer[layer].first->Check( checkIndex, true );
176 }
177}
178
179
181{
182 LSET ret;
183
184 for( unsigned layer = 0; layer < arrayDim(m_boxSelectLayer); ++layer )
185 {
186 if( !m_boxSelectLayer[layer].first )
187 continue;
188
189 if( m_boxSelectLayer[layer].first->IsChecked( m_boxSelectLayer[layer].second ) )
190 ret.set( layer );
191 }
192
193 return ret;
194}
195
196
198{
199 // Build the absolute path of current output directory to preselect it in the file browser.
200 wxString path = ExpandEnvVarSubstitutions( m_outputDirectoryName->GetValue(), &Prj() );
201 path = Prj().AbsolutePath( path );
202
203 wxDirDialog dirDialog( this, _( "Select Output Directory" ), path );
204
205 if( dirDialog.ShowModal() == wxID_CANCEL )
206 return;
207
208 wxFileName dirName = wxFileName::DirName( dirDialog.GetPath() );
209
210 wxMessageDialog dialog( this, _( "Use a relative path?" ), _( "Plot Output Directory" ),
211 wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT );
212
213 if( dialog.ShowModal() == wxID_YES )
214 {
215 wxString boardFilePath = Prj().AbsolutePath( m_board->GetFileName() );
216
217 boardFilePath = wxPathOnly( boardFilePath );
218
219 if( !dirName.MakeRelativeTo( boardFilePath ) )
220 wxMessageBox( _( "Cannot make path relative (target volume different from board "
221 "file volume)!" ),
222 _( "Plot Output Directory" ), wxOK | wxICON_ERROR );
223 }
224
225 m_outputDirectoryName->SetValue( dirName.GetFullPath() );
227}
228
229
230void DIALOG_EXPORT_SVG::onPagePerLayerClicked( wxCommandEvent& event )
231{
233
234 if( m_checkboxPagePerLayer->GetValue() )
235 {
236 m_checkboxEdgesOnAllPages->Enable( true );
238 }
239 else
240 {
241 m_checkboxEdgesOnAllPages->Enable( false );
242 m_checkboxEdgesOnAllPages->SetValue( false );
243 }
244}
245
246
247void DIALOG_EXPORT_SVG::ExportSVGFile( bool aOnlyOneFile )
248{
250
251 // Create output directory if it does not exist (also transform it in absolute form).
252 // Bail if it fails.
253
254 std::function<bool( wxString* )> textResolver =
255 [&]( wxString* token ) -> bool
256 {
257 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
258 return m_board->ResolveTextVar( token, 0 );
259 };
260
261 wxString path = m_outputDirectory;
262 path = ExpandTextVars( path, &textResolver );
263 path = ExpandEnvVarSubstitutions( path, nullptr );
264
265 wxFileName outputDir = wxFileName::DirName( path );
266 wxString boardFilename = m_board->GetFileName();
267
268 REPORTER& reporter = m_messagesPanel->Reporter();
269
270 if( !EnsureFileDirectoryExists( &outputDir, boardFilename, &reporter ) )
271 {
272 wxString msg = wxString::Format( _( "Could not write plot files to folder '%s'." ),
273 outputDir.GetPath() );
274 DisplayError( this, msg );
275 return;
276 }
277
278 m_printMirror = m_printMirrorOpt->GetValue();
279 m_printBW = m_ModeColorOption->GetSelection();
280
281 LSET all_selected = getCheckBoxSelectedLayers();
282
283 PCB_PLOT_SVG_OPTIONS svgPlotOptions;
284 svgPlotOptions.m_blackAndWhite = m_printBW;
285 svgPlotOptions.m_printMaskLayer = m_printMaskLayer;
286 svgPlotOptions.m_pageSizeMode = m_rbSvgPageSizeOpt->GetSelection();
287 svgPlotOptions.m_colorTheme = ""; // will use default
288 svgPlotOptions.m_mirror = m_printMirror;
289 svgPlotOptions.m_plotFrame = svgPlotOptions.m_pageSizeMode == 0;
290
291 for( LSEQ seq = all_selected.Seq(); seq; ++seq )
292 {
293 PCB_LAYER_ID layer = *seq;
294 wxFileName fn( boardFilename );
295 wxString suffix = aOnlyOneFile ? wxString( wxT( "brd" ) ) : m_board->GetStandardLayerName( layer );
296
297 BuildPlotFileName( &fn, outputDir.GetPath(), suffix, SVGFileExtension );
298 wxString svgPath = fn.GetFullPath();
299
300 m_printMaskLayer = aOnlyOneFile ? all_selected : LSET( layer );
301
302 if( m_checkboxEdgesOnAllPages->GetValue() )
304
305 svgPlotOptions.m_outputFile = svgPath;
306 svgPlotOptions.m_printMaskLayer = m_printMaskLayer;
307
308 if( PCB_PLOT_SVG::Plot(m_board, svgPlotOptions ) )
309 {
310 reporter.Report( wxString::Format( _( "Exported '%s'." ), svgPath ),
312 }
313 else // Error
314 {
315 reporter.Report( wxString::Format( _( "Failed to create file '%s'." ), svgPath ),
317 }
318
319 if( aOnlyOneFile )
320 break;
321 }
322}
323
324
325void DIALOG_EXPORT_SVG::OnButtonPlot( wxCommandEvent& event )
326{
329}
330
331
332bool InvokeExportSVG( PCB_EDIT_FRAME* aCaller, BOARD* aBoard )
333{
334 DIALOG_EXPORT_SVG dlg( aCaller, aBoard );
335
336 dlg.ShowModal();
337
338 return true;
339}
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:106
@ small_folder
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:269
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:587
static wxString GetStandardLayerName(PCB_LAYER_ID aLayerId)
Return an "English Standard" name of a PCB layer when given aLayerNumber.
Definition: board.h:715
bool ResolveTextVar(wxString *token, int aDepth) const
Definition: board.cpp:350
const wxString & GetFileName() const
Definition: board.h:306
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:474
Class DIALOG_EXPORT_SVG_BASE.
wxCheckListBox * m_CopperLayersList
WX_HTML_REPORT_PANEL * m_messagesPanel
STD_BITMAP_BUTTON * m_browseButton
wxCheckListBox * m_TechnicalLayersList
DIALOG_EXPORT_SVG(PCB_EDIT_FRAME *aParent, BOARD *aBoard)
void OnOutputDirectoryBrowseClicked(wxCommandEvent &event) override
PCB_EDIT_FRAME * m_parent
void OnButtonPlot(wxCommandEvent &event) override
void onPagePerLayerClicked(wxCommandEvent &event) override
LSET getCheckBoxSelectedLayers() const
std::pair< wxCheckListBox *, int > m_boxSelectLayer[PCB_LAYER_ID_COUNT]
void ExportSVGFile(bool aOnlyOneFile)
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: layer_ids.h:493
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:532
LSEQ UIOrder() const
Definition: lset.cpp:922
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
DIALOG_EXPORT_SVG m_ExportSvg
PCBNEW_SETTINGS * GetPcbNewSettings() const
The main frame for Pcbnew.
static bool Plot(BOARD *aBoard, const PCB_PLOT_SVG_OPTIONS &aSvgPlotOptions)
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition: project.cpp:305
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
void SetBitmap(const wxBitmap &aBmp)
void SetFileName(const wxString &aReportFileName)
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:299
bool EnsureFileDirectoryExists(wxFileName *aTargetFullFileName, const wxString &aBaseFilename, REPORTER *aReporter)
Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.
Definition: common.cpp:327
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:58
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:300
This file is part of the common library.
bool InvokeExportSVG(PCB_EDIT_FRAME *aCaller, BOARD *aBoard)
Function InvokeExportSVG shows the Export SVG dialog.
#define _(s)
const std::string SVGFileExtension
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:827
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ Edge_Cuts
Definition: layer_ids.h:113
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:137
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:99
void BuildPlotFileName(wxFileName *aFilename, const wxString &aOutputDir, const wxString &aSuffix, const wxString &aExtension)
Complete a plot filename.
Definition: pcbplot.cpp:361
see class PGM_BASE
Plotting engines similar to ps (PostScript, Gerber, svg)
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_ACTION
std::vector< FAB_LAYER_COLOR > dummy
Definition of file extensions used in Kicad.