diff --git a/common/jobs/job_sym_export_svg.h b/common/jobs/job_sym_export_svg.h new file mode 100644 index 0000000000..c3f85cccdf --- /dev/null +++ b/common/jobs/job_sym_export_svg.h @@ -0,0 +1,49 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright (C) 1992-2022 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 3 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, see . + */ + +#ifndef JOB_SYM_EXPORT_SVG_H +#define JOB_SYM_EXPORT_SVG_H + +#include +#include "job.h" + +class JOB_SYM_EXPORT_SVG : public JOB +{ +public: + JOB_SYM_EXPORT_SVG( bool aIsCli ) : + JOB( "symsvg", aIsCli ), + m_libraryPath(), + m_symbol(), + m_outputDirectory(), + m_blackAndWhite( false ) + { + } + + wxString m_libraryPath; + wxString m_symbol; + + wxString m_outputDirectory; + + wxString m_colorTheme; + + bool m_blackAndWhite; +}; + +#endif \ No newline at end of file diff --git a/common/plotters/SVG_plotter.cpp b/common/plotters/SVG_plotter.cpp index 110c46036d..ca947d85d8 100644 --- a/common/plotters/SVG_plotter.cpp +++ b/common/plotters/SVG_plotter.cpp @@ -742,11 +742,11 @@ bool SVG_PLOTTER::StartPlot( const wxString& aPageNumber ) strftime( date_buf, 250, "%Y/%m/%d %H:%M:%S", localtime( <ime ) ); fprintf( m_outputFile, - "SVG Picture created as %s date %s \n", + "SVG Image created as %s date %s \n", TO_UTF8( XmlEsc( wxFileName( m_filename ).GetFullName() ) ), date_buf ); // End of header - fprintf( m_outputFile, " Picture generated by %s \n", + fprintf( m_outputFile, " Image generated by %s \n", TO_UTF8( XmlEsc( m_creator ) ) ); // output the pen and brush color (RVB values in hex) and opacity diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index fcab884659..011563a688 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -183,6 +183,7 @@ const std::string JpegFileExtension( "jpg" ); const std::string TextFileExtension( "txt" ); const std::string MarkdownFileExtension( "md" ); const std::string CsvFileExtension( "csv" ); +const std::string XmlFileExtension( "xml" ); const wxString GerberFileExtensionsRegex( "(gbr|gko|pho|(g[tb][alops])|(gm?\\d\\d*)|(gp[tb]))" ); diff --git a/eeschema/eeschema_jobs_handler.cpp b/eeschema/eeschema_jobs_handler.cpp index 3dcec24c8d..3d3ab25f79 100644 --- a/eeschema/eeschema_jobs_handler.cpp +++ b/eeschema/eeschema_jobs_handler.cpp @@ -24,17 +24,22 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include "eeschema_helpers.h" #include +#include #include #include +#include #include @@ -62,7 +67,9 @@ EESCHEMA_JOBS_HANDLER::EESCHEMA_JOBS_HANDLER() Register( "svg", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportSvg, this, std::placeholders::_1 ) ); Register( "symupgrade", - std::bind( &EESCHEMA_JOBS_HANDLER::JobExportSymLibUpgrade, this, std::placeholders::_1 ) ); + std::bind( &EESCHEMA_JOBS_HANDLER::JobSymUpgrade, this, std::placeholders::_1 ) ); + Register( "symsvg", + std::bind( &EESCHEMA_JOBS_HANDLER::JobSymExportSvg, this, std::placeholders::_1 ) ); } @@ -302,13 +309,11 @@ int EESCHEMA_JOBS_HANDLER::JobExportPythonBom( JOB* aJob ) std::unique_ptr xmlNetlist = std::make_unique( sch ); - wxString fileExt = wxS( "xml" ); - if( aNetJob->m_outputFile.IsEmpty() ) { wxFileName fn = sch->GetFileName(); fn.SetName( fn.GetName() + "-bom" ); - fn.SetExt( fileExt ); + fn.SetExt( XmlFileExtension ); aNetJob->m_outputFile = fn.GetFullName(); } @@ -324,7 +329,158 @@ int EESCHEMA_JOBS_HANDLER::JobExportPythonBom( JOB* aJob ) } -int EESCHEMA_JOBS_HANDLER::JobExportSymLibUpgrade( JOB* aJob ) +int EESCHEMA_JOBS_HANDLER::doSymExportSvg( JOB_SYM_EXPORT_SVG* aSvgJob, + KIGFX::SCH_RENDER_SETTINGS* aRenderSettings, + LIB_SYMBOL* symbol ) +{ + wxASSERT( symbol != nullptr ); + + if( symbol == nullptr ) + return CLI::EXIT_CODES::ERR_UNKNOWN; + + // iterate from unit 1, unit 0 would be "all units" which we don't want + for( int unit = 1; unit < symbol->GetUnitCount() + 1; unit++ ) + { + int convert = 0; + + wxFileName fn; + + fn.SetPath( aSvgJob->m_outputDirectory ); + fn.SetExt( SVGFileExtension ); + + //simplify the name if its single unit + if( symbol->IsMulti() ) + { + fn.SetName( wxString::Format( "%s_%d", symbol->GetName().Lower(), unit ) ); + wxPrintf( _( "Plotting symbol '%s' unit %d to '%s'\n" ), symbol->GetName(), unit, + fn.GetFullPath() ); + } + else + { + fn.SetName( symbol->GetName().Lower() ); + wxPrintf( _( "Plotting symbol '%s' to '%s'\n" ), symbol->GetName(), fn.GetFullPath() ); + } + + // Get the symbol bounding box to fit the plot page to it + BOX2I symbolBB = symbol->Flatten()->GetUnitBoundingBox( unit, convert ); + PAGE_INFO pageInfo( PAGE_INFO::Custom ); + pageInfo.SetHeightMils( schIUScale.IUToMils( symbolBB.GetHeight() * 1.2 ) ); + pageInfo.SetWidthMils( schIUScale.IUToMils( symbolBB.GetWidth() * 1.2 ) ); + + SVG_PLOTTER* plotter = new SVG_PLOTTER(); + plotter->SetRenderSettings( aRenderSettings ); + plotter->SetPageSettings( pageInfo ); + plotter->SetColorMode( !aSvgJob->m_blackAndWhite ); + + wxPoint plot_offset; + const double scale = 1.0; + + // Currently, plot units are in decimil + plotter->SetViewport( plot_offset, schIUScale.IU_PER_MILS / 10, scale, false ); + + plotter->SetCreator( wxT( "Eeschema-SVG" ) ); + + if( !plotter->OpenFile( fn.GetFullPath() ) ) + { + wxFprintf( stderr, _( "Unable to open destination '%s'" ), fn.GetFullPath() ); + + delete plotter; + return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE; + } + + LOCALE_IO toggle; + + plotter->StartPlot( wxT( "1" ) ); + + if( symbol ) + { + constexpr bool background = true; + TRANSFORM temp; // Uses default transform + VECTOR2I plotPos; + + plotPos.x = pageInfo.GetWidthIU( schIUScale.IU_PER_MILS ) / 2; + plotPos.y = pageInfo.GetHeightIU( schIUScale.IU_PER_MILS ) / 2; + + symbol->Plot( plotter, unit, convert, background, plotPos, temp, false ); + + // Plot lib fields, not plotted by m_symbol->Plot(): + symbol->PlotLibFields( plotter, unit, convert, background, plotPos, temp, false ); + + symbol->Plot( plotter, unit, convert, !background, plotPos, temp, false ); + + // Plot lib fields, not plotted by m_symbol->Plot(): + symbol->PlotLibFields( plotter, unit, convert, !background, plotPos, temp, false ); + } + + plotter->EndPlot(); + delete plotter; + } + + return CLI::EXIT_CODES::OK; +} + + +int EESCHEMA_JOBS_HANDLER::JobSymExportSvg( JOB* aJob ) +{ + JOB_SYM_EXPORT_SVG* svgJob = dynamic_cast( aJob ); + + SCH_SEXPR_PLUGIN_CACHE schLibrary( svgJob->m_libraryPath ); + + try + { + schLibrary.Load(); + } + catch( ... ) + { + wxFprintf( stderr, _( "Unable to load library\n" ) ); + return CLI::EXIT_CODES::ERR_UNKNOWN; + } + + LIB_SYMBOL* symbol = nullptr; + if( !svgJob->m_symbol.IsEmpty() ) + { + // See if the selected symbol exists + symbol = schLibrary.GetSymbol( svgJob->m_symbol ); + if( !symbol ) + { + wxFprintf( stderr, _( "There is no symbol selected to save." ) ); + return CLI::EXIT_CODES::ERR_ARGS; + } + } + + if( !svgJob->m_outputDirectory.IsEmpty() && !wxDir::Exists( svgJob->m_outputDirectory ) ) + { + wxFileName::Mkdir( svgJob->m_outputDirectory ); + } + + KIGFX::SCH_RENDER_SETTINGS renderSettings; + COLOR_SETTINGS* cs = Pgm().GetSettingsManager().GetColorSettings( svgJob->m_colorTheme ); + renderSettings.LoadColors( cs ); + renderSettings.SetDefaultPenWidth( DEFAULT_LINE_WIDTH_MILS * schIUScale.IU_PER_MILS ); + + int exitCode = CLI::EXIT_CODES::OK; + if( symbol ) + { + exitCode = doSymExportSvg( svgJob, &renderSettings, symbol ); + } + else + { + // Just plot all the symbols we can + const LIB_SYMBOL_MAP& libSymMap = schLibrary.GetSymbolMap(); + + for( const std::pair& entry : libSymMap ) + { + exitCode = doSymExportSvg( svgJob, &renderSettings, entry.second ); + if( exitCode != CLI::EXIT_CODES::OK ) + break; + } + } + + return exitCode; +} + + +int EESCHEMA_JOBS_HANDLER::JobSymUpgrade( JOB* aJob ) { JOB_SYM_UPGRADE* upgradeJob = dynamic_cast( aJob ); diff --git a/eeschema/eeschema_jobs_handler.h b/eeschema/eeschema_jobs_handler.h index a06e853139..43c4af2d89 100644 --- a/eeschema/eeschema_jobs_handler.h +++ b/eeschema/eeschema_jobs_handler.h @@ -31,6 +31,8 @@ class SCH_RENDER_SETTINGS; }; class SCHEMATIC; +class JOB_SYM_EXPORT_SVG; +class LIB_SYMBOL; /** * Handles eeschema job dispatches @@ -43,7 +45,10 @@ public: int JobExportNetlist( JOB* aJob ); int JobExportPdf( JOB* aJob ); int JobExportSvg( JOB* aJob ); - int JobExportSymLibUpgrade( JOB* aJob ); + int JobSymUpgrade( JOB* aJob ); + int JobSymExportSvg( JOB* aJob ); + + int doSymExportSvg( JOB_SYM_EXPORT_SVG* aSvgJob, KIGFX::SCH_RENDER_SETTINGS* aRenderSettings, LIB_SYMBOL* symbol ); /** * Configures the SCH_RENDER_SETTINGS object with the correct data to be used with plotting diff --git a/eeschema/sch_plugins/sch_lib_plugin_cache.cpp b/eeschema/sch_plugins/sch_lib_plugin_cache.cpp index 1e97697c53..b6d2b42f14 100644 --- a/eeschema/sch_plugins/sch_lib_plugin_cache.cpp +++ b/eeschema/sch_plugins/sch_lib_plugin_cache.cpp @@ -173,3 +173,16 @@ void SCH_LIB_PLUGIN_CACHE::AddSymbol( const LIB_SYMBOL* aSymbol ) m_isModified = true; IncrementModifyHash(); } + + +LIB_SYMBOL* SCH_LIB_PLUGIN_CACHE::GetSymbol( const wxString& aName ) +{ + LIB_SYMBOL_MAP::iterator it = m_symbols.find( aName ); + + if( it != m_symbols.end() ) + { + return it->second; + } + + return nullptr; +} \ No newline at end of file diff --git a/eeschema/sch_plugins/sch_lib_plugin_cache.h b/eeschema/sch_plugins/sch_lib_plugin_cache.h index 020eb659f5..130cae2743 100644 --- a/eeschema/sch_plugins/sch_lib_plugin_cache.h +++ b/eeschema/sch_plugins/sch_lib_plugin_cache.h @@ -68,6 +68,8 @@ public: virtual void DeleteSymbol( const wxString& aName ) = 0; + virtual LIB_SYMBOL* GetSymbol( const wxString& aName ); + // If m_libFileName is a symlink follow it to the real source file wxFileName GetRealFile() const; @@ -85,6 +87,8 @@ public: wxString GetFileName() const { return m_libFileName.GetFullPath(); } + const LIB_SYMBOL_MAP& GetSymbolMap() const { return m_symbols; } + protected: LIB_SYMBOL* removeSymbol( LIB_SYMBOL* aAlias ); diff --git a/eeschema/symbol_editor/symbol_editor_plotter.cpp b/eeschema/symbol_editor/symbol_editor_plotter.cpp index d2d75a7d75..0732a34659 100644 --- a/eeschema/symbol_editor/symbol_editor_plotter.cpp +++ b/eeschema/symbol_editor/symbol_editor_plotter.cpp @@ -40,7 +40,7 @@ void SYMBOL_EDIT_FRAME::SVGPlotSymbol( const wxString& aFullFileName ) plotter->SetPageSettings( pageInfo ); plotter->SetColorMode( true ); - wxPoint plot_offset; + VECTOR2I plot_offset; const double scale = 1.0; // Currently, plot units are in decimil diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h index 38685ae2d1..3482d16872 100644 --- a/include/wildcards_and_files_ext.h +++ b/include/wildcards_and_files_ext.h @@ -169,6 +169,7 @@ extern const std::string JpegFileExtension; extern const std::string TextFileExtension; extern const std::string MarkdownFileExtension; extern const std::string CsvFileExtension; +extern const std::string XmlFileExtension; extern const wxString GerberFileExtensionsRegex; diff --git a/kicad/CMakeLists.txt b/kicad/CMakeLists.txt index b31fcd06b8..1af44b6862 100644 --- a/kicad/CMakeLists.txt +++ b/kicad/CMakeLists.txt @@ -31,6 +31,7 @@ set( KICAD_SRCS cli/command_export_sch_netlist.cpp cli/command_export_sch_pdf.cpp cli/command_export_sch_svg.cpp + cli/command_sym_export_svg.cpp cli/command_sym_upgrade.cpp dialogs/dialog_template_selector_base.cpp dialogs/dialog_template_selector.cpp diff --git a/kicad/cli/command_sym_export.h b/kicad/cli/command_sym_export.h new file mode 100644 index 0000000000..d50d1c6dd6 --- /dev/null +++ b/kicad/cli/command_sym_export.h @@ -0,0 +1,34 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright (C) 1992-2022 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 3 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, see . + */ + +#ifndef COMMAND_SYM_EXPORT_H +#define COMMAND_SYM_EXPORT_H + +#include "command.h" + +namespace CLI +{ +struct SYM_EXPORT_COMMAND : public COMMAND +{ + SYM_EXPORT_COMMAND() : COMMAND( "export" ) {} +}; +} + +#endif \ No newline at end of file diff --git a/kicad/cli/command_sym_export_svg.cpp b/kicad/cli/command_sym_export_svg.cpp new file mode 100644 index 0000000000..34fa39c453 --- /dev/null +++ b/kicad/cli/command_sym_export_svg.cpp @@ -0,0 +1,71 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright (C) 1992-2022 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 3 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, see . + */ + +#include "command_sym_export_svg.h" +#include +#include "jobs/job_sym_export_svg.h" +#include +#include +#include + +#include +#include + +#define ARG_NO_BACKGROUND_COLOR "--no-background-color" +#define ARG_SYMBOL "--symbol" + +CLI::SYM_EXPORT_SVG_COMMAND::SYM_EXPORT_SVG_COMMAND() : EXPORT_PCB_BASE_COMMAND( "svg" ) +{ + m_argParser.add_argument( "-t", ARG_THEME ) + .default_value( std::string() ) + .help( UTF8STDSTR( _( "Color theme to use (will default to pcbnew settings)" ) ) ); + + m_argParser.add_argument( "-s", ARG_SYMBOL ) + .default_value( std::string() ) + .help( UTF8STDSTR( _( "Specific symbol to export within the library" ) ) ); + + m_argParser.add_argument( ARG_BLACKANDWHITE ) + .help( UTF8STDSTR( _( "Black and white only" ) ) ) + .implicit_value( true ) + .default_value( false ); +} + + +int CLI::SYM_EXPORT_SVG_COMMAND::Perform( KIWAY& aKiway ) +{ + std::unique_ptr svgJob = std::make_unique( true ); + + svgJob->m_libraryPath = FROM_UTF8( m_argParser.get( ARG_INPUT ).c_str() ); + svgJob->m_outputDirectory = FROM_UTF8( m_argParser.get( ARG_OUTPUT ).c_str() ); + svgJob->m_blackAndWhite = m_argParser.get( ARG_BLACKANDWHITE ); + svgJob->m_symbol = FROM_UTF8( m_argParser.get( ARG_SYMBOL ).c_str() ); + + if( !wxFile::Exists( svgJob->m_libraryPath ) ) + { + wxFprintf( stderr, _( "Symbol file does not exist or is not accessible\n" ) ); + return EXIT_CODES::ERR_INVALID_INPUT_FILE; + } + + svgJob->m_colorTheme = FROM_UTF8( m_argParser.get( ARG_THEME ).c_str() ); + + int exitCode = aKiway.ProcessJob( KIWAY::FACE_SCH, svgJob.get() ); + + return exitCode; +} \ No newline at end of file diff --git a/kicad/cli/command_sym_export_svg.h b/kicad/cli/command_sym_export_svg.h new file mode 100644 index 0000000000..26da761194 --- /dev/null +++ b/kicad/cli/command_sym_export_svg.h @@ -0,0 +1,37 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright (C) 1992-2022 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 3 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, see . + */ + +#ifndef COMMAND_SYM_EXPORT_SVG_H +#define COMMAND_SYM_EXPORT_SVG_H + +#include "command_export_pcb_base.h" + +namespace CLI +{ +class SYM_EXPORT_SVG_COMMAND : public EXPORT_PCB_BASE_COMMAND +{ +public: + SYM_EXPORT_SVG_COMMAND(); + + int Perform( KIWAY& aKiway ) override; +}; +} // namespace CLI + +#endif \ No newline at end of file diff --git a/kicad/kicad_cli.cpp b/kicad/kicad_cli.cpp index aeffa8716e..4fef69d1e0 100644 --- a/kicad/kicad_cli.cpp +++ b/kicad/kicad_cli.cpp @@ -66,6 +66,8 @@ #include "cli/command_sch.h" #include "cli/command_sch_export.h" #include "cli/command_sym.h" +#include "cli/command_sym_export.h" +#include "cli/command_sym_export_svg.h" #include "cli/command_sym_upgrade.h" #include "cli/exit_codes.h" #include "cli/cli_names.h" @@ -134,6 +136,8 @@ static CLI::EXPORT_SCH_SVG_COMMAND exportSchSvgCmd{}; static CLI::FP_COMMAND fpCmd{}; static CLI::FP_UPGRADE_COMMAND fpUpgradeCmd{}; static CLI::SYM_COMMAND symCmd{}; +static CLI::SYM_EXPORT_COMMAND symExportCmd{}; +static CLI::SYM_EXPORT_SVG_COMMAND symExportSvgCmd{}; static CLI::SYM_UPGRADE_COMMAND symUpgradeCmd{}; static std::vector commandStack = { @@ -148,7 +152,8 @@ static std::vector commandStack = { { &pcbCmd, { - { &exportPcbCmd, + { + &exportPcbCmd, { &exportPcbDrillCmd, &exportPcbDxfCmd, @@ -165,7 +170,8 @@ static std::vector commandStack = { { &schCmd, { - { &exportSchCmd, + { + &exportSchCmd, { &exportSchNetlistCmd, &exportSchPdfCmd, @@ -178,6 +184,12 @@ static std::vector commandStack = { { &symCmd, { + { + &symExportCmd, + { + &symExportSvgCmd + } + }, { &symUpgradeCmd }