You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

897 lines
31 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2018 Jean-Pierre Charras jp.charras at wanadoo.fr
  5. * Copyright (C) 1992-2010 Lorenzo Marcantonio
  6. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <wx/log.h>
  27. #include <common.h>
  28. #include <sch_plotter.h>
  29. #include <locale_io.h>
  30. #include <plotters/plotter_dxf.h>
  31. #include <plotters/plotters_pslike.h>
  32. #include <pgm_base.h>
  33. #include <trace_helpers.h>
  34. #include <sch_edit_frame.h>
  35. #include <sch_painter.h>
  36. #include <schematic.h>
  37. #include <sch_screen.h>
  38. #include <settings/settings_manager.h>
  39. // Note:
  40. // We need to switch between sheets to plot a hierarchy and update references and sheet number
  41. // Use SCHEMATIC::SetCurrentSheet( xxx ) to switch to a sheet.
  42. // Do not use SCH_EDIT_FRAME::SetCurrentSheet( xxx ) to switch to a sheet, because the new sheet
  43. // is not displayed, but SCH_EDIT_FRAME::SetCurrentSheet() has side effects to the current VIEW
  44. // (clear some data used to show the sheet on screen) and does not fully restore the "old" screen
  45. SCH_PLOTTER::SCH_PLOTTER( SCHEMATIC* aSchematic ) :
  46. m_schematic( aSchematic )
  47. {
  48. m_colorSettings = nullptr;
  49. }
  50. SCH_PLOTTER::SCH_PLOTTER( SCH_EDIT_FRAME* aFrame ) :
  51. m_schematic( &aFrame->Schematic() )
  52. {
  53. m_colorSettings = nullptr;
  54. }
  55. wxFileName SCH_PLOTTER::getOutputFilenameSingle( const SCH_PLOT_OPTS& aPlotOpts,
  56. REPORTER* aReporter, const wxString& aExt )
  57. {
  58. if( !aPlotOpts.m_outputFile.empty() )
  59. {
  60. return aPlotOpts.m_outputFile;
  61. }
  62. else
  63. {
  64. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  65. // The sub sheet can be in a sub_hierarchy, but we plot the file in the main
  66. // project folder (or the folder specified by the caller), so replace separators
  67. // to create a unique filename:
  68. fname.Replace( "/", "_" );
  69. fname.Replace( "\\", "_" );
  70. return createPlotFileName( aPlotOpts, fname, aExt, aReporter );
  71. }
  72. }
  73. void SCH_PLOTTER::createPDFFile( const SCH_PLOT_OPTS& aPlotOpts,
  74. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  75. {
  76. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet(); // sheetpath is saved here
  77. /* When printing all pages, the printed page is not the current page. In complex hierarchies,
  78. * we must update symbol references and other parameters in the given printed SCH_SCREEN,
  79. * according to the sheet path because in complex hierarchies a SCH_SCREEN (a drawing ) is
  80. * shared between many sheets and symbol references depend on the actual sheet path used.
  81. */
  82. SCH_SHEET_LIST sheetList;
  83. if( aPlotOpts.m_plotAll || aPlotOpts.m_plotPages.size() > 0 )
  84. {
  85. sheetList.BuildSheetList( &m_schematic->Root(), true );
  86. sheetList.SortByHierarchicalPageNumbers();
  87. // remove the non-selected pages if we are in plot pages mode
  88. if( aPlotOpts.m_plotPages.size() > 0 )
  89. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  90. }
  91. else
  92. {
  93. // in Eeschema, this prints the current page
  94. sheetList.push_back( m_schematic->CurrentSheet() );
  95. }
  96. if( sheetList.empty() )
  97. {
  98. if( aReporter )
  99. aReporter->Report( _( "No sheets to plot." ), RPT_SEVERITY_ERROR );
  100. return;
  101. }
  102. wxCHECK( m_schematic, /* void */ );
  103. // Allocate the plotter and set the job level parameter
  104. PDF_PLOTTER* plotter = new PDF_PLOTTER( &m_schematic->Prj() );
  105. plotter->SetRenderSettings( aRenderSettings );
  106. plotter->SetColorMode( !aPlotOpts.m_blackAndWhite );
  107. plotter->SetCreator( wxT( "Eeschema-PDF" ) );
  108. plotter->SetTitle( ExpandTextVars( m_schematic->RootScreen()->GetTitleBlock().GetTitle(),
  109. &m_schematic->Prj() ) );
  110. wxString msg;
  111. wxFileName plotFileName;
  112. LOCALE_IO toggle; // Switch the locale to standard C
  113. for( unsigned i = 0; i < sheetList.size(); i++ )
  114. {
  115. m_schematic->SetCurrentSheet( sheetList[i] );
  116. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  117. m_schematic->SetSheetNumberAndCount();
  118. SCH_SCREEN* screen = m_schematic->CurrentSheet().LastScreen();
  119. wxString sheetName = sheetList[i].Last()->GetField( FIELD_T::SHEET_NAME )->GetShownText( false );
  120. if( aPlotOpts.m_PDFMetadata )
  121. {
  122. msg = wxS( "AUTHOR" );
  123. if( m_schematic->ResolveTextVar( &sheetList[i], &msg, 0 ) )
  124. plotter->SetAuthor( msg );
  125. msg = wxS( "SUBJECT" );
  126. if( m_schematic->ResolveTextVar( &sheetList[i], &msg, 0 ) )
  127. plotter->SetSubject( msg );
  128. }
  129. if( i == 0 )
  130. {
  131. try
  132. {
  133. wxString ext = PDF_PLOTTER::GetDefaultFileExtension();
  134. plotFileName = getOutputFilenameSingle( aPlotOpts, aReporter, ext );
  135. m_lastOutputFilePath = plotFileName.GetFullPath();
  136. if( !plotFileName.IsOk() )
  137. return;
  138. if( !plotter->OpenFile( plotFileName.GetFullPath() ) )
  139. {
  140. if( aReporter )
  141. {
  142. msg.Printf( _( "Failed to create file '%s'." ),
  143. plotFileName.GetFullPath() );
  144. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  145. }
  146. delete plotter;
  147. return;
  148. }
  149. // Open the plotter and do the first page
  150. setupPlotPagePDF( plotter, screen, aPlotOpts );
  151. plotter->StartPlot( sheetList[i].GetPageNumber(), sheetName );
  152. }
  153. catch( const IO_ERROR& e )
  154. {
  155. // Cannot plot PDF file
  156. if( aReporter )
  157. {
  158. msg.Printf( wxT( "PDF Plotter exception: %s" ), e.What() );
  159. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  160. }
  161. restoreEnvironment( plotter, oldsheetpath );
  162. return;
  163. }
  164. }
  165. else
  166. {
  167. /* For the following pages you need to close the (finished) page,
  168. * reconfigure, and then start a new one */
  169. plotter->ClosePage();
  170. setupPlotPagePDF( plotter, screen, aPlotOpts );
  171. SCH_SHEET_PATH parentSheet = sheetList[i];
  172. if( parentSheet.size() > 1 )
  173. {
  174. // The sheet path is the full path to the sheet, so we need to remove the last
  175. // sheet name to get the parent sheet path
  176. parentSheet.pop_back();
  177. }
  178. wxString parentSheetName =
  179. parentSheet.Last()->GetField( FIELD_T::SHEET_NAME )->GetShownText( false );
  180. plotter->StartPage( sheetList[i].GetPageNumber(), sheetName,
  181. parentSheet.GetPageNumber(), parentSheetName );
  182. }
  183. plotOneSheetPDF( plotter, screen, aPlotOpts );
  184. }
  185. // Everything done, close the plot and restore the environment
  186. if( aReporter )
  187. {
  188. msg.Printf( _( "Plotted to '%s'.\n" ), plotFileName.GetFullPath() );
  189. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  190. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  191. }
  192. restoreEnvironment( plotter, oldsheetpath );
  193. }
  194. void SCH_PLOTTER::plotOneSheetPDF( PLOTTER* aPlotter, SCH_SCREEN* aScreen,
  195. const SCH_PLOT_OPTS& aPlotOpts )
  196. {
  197. if( aPlotOpts.m_useBackgroundColor && aPlotter->GetColorMode() )
  198. {
  199. aPlotter->SetColor( aPlotter->RenderSettings()->GetBackgroundColor() );
  200. // Use page size selected in schematic to know the schematic bg area
  201. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  202. VECTOR2I end( actualPage.GetWidthIU( schIUScale.IU_PER_MILS ),
  203. actualPage.GetHeightIU( schIUScale.IU_PER_MILS ) );
  204. aPlotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 );
  205. }
  206. if( aPlotOpts.m_plotDrawingSheet )
  207. {
  208. COLOR4D color = COLOR4D::BLACK;
  209. if( aPlotter->GetColorMode() )
  210. color = aPlotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET );
  211. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  212. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  213. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  214. PlotDrawingSheet( aPlotter, &aScreen->Schematic()->Prj(),
  215. aScreen->GetTitleBlock(),
  216. actualPage,
  217. aScreen->Schematic()->GetProperties(),
  218. aScreen->GetPageNumber(), aScreen->GetPageCount(), sheetName, sheetPath,
  219. aScreen->GetFileName(), color, aScreen->GetVirtualPageNumber() == 1 );
  220. }
  221. aScreen->Plot( aPlotter, aPlotOpts );
  222. }
  223. void SCH_PLOTTER::setupPlotPagePDF( PLOTTER* aPlotter, SCH_SCREEN* aScreen,
  224. const SCH_PLOT_OPTS& aPlotOpts )
  225. {
  226. PAGE_INFO plotPage; // page size selected to plot
  227. // Considerations on page size and scaling requests
  228. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  229. switch( aPlotOpts.m_pageSizeSelect )
  230. {
  231. case PAGE_SIZE_A:
  232. plotPage.SetType( wxT( "A" ) );
  233. plotPage.SetPortrait( actualPage.IsPortrait() );
  234. break;
  235. case PAGE_SIZE_A4:
  236. plotPage.SetType( wxT( "A4" ) );
  237. plotPage.SetPortrait( actualPage.IsPortrait() );
  238. break;
  239. case PAGE_SIZE_AUTO:
  240. default:
  241. plotPage = actualPage;
  242. break;
  243. }
  244. double scalex = (double) plotPage.GetWidthMils() / actualPage.GetWidthMils();
  245. double scaley = (double) plotPage.GetHeightMils() / actualPage.GetHeightMils();
  246. double scale = std::min( scalex, scaley );
  247. aPlotter->SetPageSettings( plotPage );
  248. // Currently, plot units are in decimil
  249. aPlotter->SetViewport( VECTOR2I( 0, 0 ), schIUScale.IU_PER_MILS / 10, scale, false );
  250. }
  251. void SCH_PLOTTER::createPSFiles( const SCH_PLOT_OPTS& aPlotOpts,
  252. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  253. {
  254. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet(); // sheetpath is saved here
  255. PAGE_INFO plotPage; // page size selected to plot
  256. wxString msg;
  257. /* When printing all pages, the printed page is not the current page.
  258. * In complex hierarchies, we must update symbol references and other parameters in the
  259. * given printed SCH_SCREEN, accordant to the sheet path because in complex hierarchies
  260. * a SCH_SCREEN (a drawing ) is shared between many sheets and symbol references
  261. * depend on the actual sheet path used.
  262. */
  263. SCH_SHEET_LIST sheetList;
  264. if( aPlotOpts.m_plotAll )
  265. {
  266. sheetList.BuildSheetList( &m_schematic->Root(), true );
  267. sheetList.SortByPageNumbers();
  268. // remove the non-selected pages if we are in plot pages mode
  269. if( aPlotOpts.m_plotPages.size() > 0 )
  270. {
  271. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  272. }
  273. }
  274. else
  275. {
  276. sheetList.push_back( m_schematic->CurrentSheet() );
  277. }
  278. for( unsigned i = 0; i < sheetList.size(); i++ )
  279. {
  280. m_schematic->SetCurrentSheet( sheetList[i] );
  281. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  282. m_schematic->SetSheetNumberAndCount();
  283. SCH_SCREEN* screen = m_schematic->CurrentSheet().LastScreen();
  284. PAGE_INFO actualPage = screen->GetPageSettings();
  285. switch( aPlotOpts.m_pageSizeSelect )
  286. {
  287. case PAGE_SIZE_A:
  288. plotPage.SetType( wxT( "A" ) );
  289. plotPage.SetPortrait( actualPage.IsPortrait() );
  290. break;
  291. case PAGE_SIZE_A4:
  292. plotPage.SetType( wxT( "A4" ) );
  293. plotPage.SetPortrait( actualPage.IsPortrait() );
  294. break;
  295. case PAGE_SIZE_AUTO:
  296. default:
  297. plotPage = actualPage;
  298. break;
  299. }
  300. double scalex = (double) plotPage.GetWidthMils() / actualPage.GetWidthMils();
  301. double scaley = (double) plotPage.GetHeightMils() / actualPage.GetHeightMils();
  302. double scale = std::min( scalex, scaley );
  303. VECTOR2I plot_offset;
  304. try
  305. {
  306. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  307. // The sub sheet can be in a sub_hierarchy, but we plot the file in the
  308. // main project folder (or the folder specified by the caller),
  309. // so replace separators to create a unique filename:
  310. fname.Replace( "/", "_" );
  311. fname.Replace( "\\", "_" );
  312. wxString ext = PS_PLOTTER::GetDefaultFileExtension();
  313. wxFileName plotFileName = createPlotFileName( aPlotOpts, fname, ext, aReporter );
  314. m_lastOutputFilePath = plotFileName.GetFullPath();
  315. if( !plotFileName.IsOk() )
  316. {
  317. if( aReporter )
  318. {
  319. // Error
  320. msg.Printf( _( "Failed to create file '%s'." ), plotFileName.GetFullPath() );
  321. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  322. }
  323. }
  324. else if( plotOneSheetPS( plotFileName.GetFullPath(), screen, aRenderSettings,
  325. actualPage, plot_offset, scale, aPlotOpts ) )
  326. {
  327. if( aReporter )
  328. {
  329. msg.Printf( _( "Plotted to '%s'." ), plotFileName.GetFullPath() );
  330. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  331. }
  332. }
  333. else
  334. {
  335. if( aReporter )
  336. {
  337. // Error
  338. msg.Printf( _( "Failed to create file '%s'." ), plotFileName.GetFullPath() );
  339. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  340. }
  341. }
  342. }
  343. catch( IO_ERROR& e )
  344. {
  345. if( aReporter )
  346. {
  347. msg.Printf( wxT( "PS Plotter exception: %s" ), e.What() );
  348. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  349. }
  350. }
  351. }
  352. if( aReporter )
  353. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  354. restoreEnvironment( nullptr, oldsheetpath );
  355. }
  356. bool SCH_PLOTTER::plotOneSheetPS( const wxString& aFileName, SCH_SCREEN* aScreen,
  357. RENDER_SETTINGS* aRenderSettings, const PAGE_INFO& aPageInfo,
  358. const VECTOR2I& aPlot0ffset, double aScale,
  359. const SCH_PLOT_OPTS& aPlotOpts )
  360. {
  361. PS_PLOTTER* plotter = new PS_PLOTTER();
  362. plotter->SetRenderSettings( aRenderSettings );
  363. plotter->SetPageSettings( aPageInfo );
  364. plotter->SetColorMode( !aPlotOpts.m_blackAndWhite );
  365. // Currently, plot units are in decimil
  366. plotter->SetViewport( aPlot0ffset, schIUScale.IU_PER_MILS / 10, aScale, false );
  367. // Init :
  368. plotter->SetCreator( wxT( "Eeschema-PS" ) );
  369. if( !plotter->OpenFile( aFileName ) )
  370. {
  371. delete plotter;
  372. return false;
  373. }
  374. LOCALE_IO toggle; // Switch the locale to standard C
  375. plotter->StartPlot( m_schematic->CurrentSheet().GetPageNumber() );
  376. if( aPlotOpts.m_useBackgroundColor && plotter->GetColorMode() )
  377. {
  378. plotter->SetColor( plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_BACKGROUND ) );
  379. // Use page size selected in schematic to know the schematic bg area
  380. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  381. VECTOR2I end( actualPage.GetWidthIU( schIUScale.IU_PER_MILS ),
  382. actualPage.GetHeightIU( schIUScale.IU_PER_MILS ) );
  383. plotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 );
  384. }
  385. if( aPlotOpts.m_plotDrawingSheet )
  386. {
  387. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  388. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  389. COLOR4D color = plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET );
  390. PlotDrawingSheet( plotter, &aScreen->Schematic()->Prj(),
  391. aScreen->GetTitleBlock(),
  392. aPageInfo, aScreen->Schematic()->GetProperties(),
  393. aScreen->GetPageNumber(), aScreen->GetPageCount(), sheetName, sheetPath,
  394. aScreen->GetFileName(), plotter->GetColorMode() ? color : COLOR4D::BLACK,
  395. aScreen->GetVirtualPageNumber() == 1 );
  396. }
  397. aScreen->Plot( plotter, aPlotOpts );
  398. plotter->EndPlot();
  399. delete plotter;
  400. return true;
  401. }
  402. void SCH_PLOTTER::createSVGFiles( const SCH_PLOT_OPTS& aPlotOpts,
  403. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  404. {
  405. wxString msg;
  406. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet();
  407. SCH_SHEET_LIST sheetList;
  408. if( aPlotOpts.m_plotAll )
  409. {
  410. sheetList.BuildSheetList( &m_schematic->Root(), true );
  411. sheetList.SortByPageNumbers();
  412. // remove the non-selected pages if we are in plot pages mode
  413. if( aPlotOpts.m_plotPages.size() > 0 )
  414. {
  415. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  416. }
  417. }
  418. else
  419. {
  420. // in Eeschema, this prints the current page
  421. sheetList.push_back( m_schematic->CurrentSheet() );
  422. }
  423. for( unsigned i = 0; i < sheetList.size(); i++ )
  424. {
  425. SCH_SCREEN* screen;
  426. m_schematic->SetCurrentSheet( sheetList[i] );
  427. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  428. m_schematic->SetSheetNumberAndCount();
  429. screen = m_schematic->CurrentSheet().LastScreen();
  430. try
  431. {
  432. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  433. // The sub sheet can be in a sub_hierarchy, but we plot the file in the
  434. // main project folder (or the folder specified by the caller),
  435. // so replace separators to create a unique filename:
  436. fname.Replace( "/", "_" );
  437. fname.Replace( "\\", "_" );
  438. wxString ext = SVG_PLOTTER::GetDefaultFileExtension();
  439. wxFileName plotFileName = createPlotFileName( aPlotOpts, fname, ext, aReporter );
  440. m_lastOutputFilePath = plotFileName.GetFullPath();
  441. if( !plotFileName.IsOk() )
  442. return;
  443. bool success = plotOneSheetSVG( plotFileName.GetFullPath(), screen, aRenderSettings,
  444. aPlotOpts );
  445. if( !success )
  446. {
  447. if( aReporter )
  448. {
  449. msg.Printf( _( "Failed to create file '%s'." ), plotFileName.GetFullPath() );
  450. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  451. }
  452. }
  453. else
  454. {
  455. if( aReporter )
  456. {
  457. msg.Printf( _( "Plotted to '%s'." ), plotFileName.GetFullPath() );
  458. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  459. }
  460. }
  461. }
  462. catch( const IO_ERROR& e )
  463. {
  464. if( aReporter )
  465. {
  466. // Cannot plot SVG file
  467. msg.Printf( wxT( "SVG Plotter exception: %s" ), e.What() );
  468. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  469. }
  470. break;
  471. }
  472. }
  473. if( aReporter )
  474. {
  475. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  476. }
  477. restoreEnvironment( nullptr, oldsheetpath );
  478. }
  479. bool SCH_PLOTTER::plotOneSheetSVG( const wxString& aFileName, SCH_SCREEN* aScreen,
  480. RENDER_SETTINGS* aRenderSettings,
  481. const SCH_PLOT_OPTS& aPlotOpts )
  482. {
  483. PAGE_INFO plotPage;
  484. // Adjust page size and scaling requests
  485. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  486. switch( aPlotOpts.m_pageSizeSelect )
  487. {
  488. case PAGE_SIZE_A:
  489. plotPage.SetType( wxT( "A" ) );
  490. plotPage.SetPortrait( actualPage.IsPortrait() );
  491. break;
  492. case PAGE_SIZE_A4:
  493. plotPage.SetType( wxT( "A4" ) );
  494. plotPage.SetPortrait( actualPage.IsPortrait() );
  495. break;
  496. case PAGE_SIZE_AUTO:
  497. default:
  498. plotPage = actualPage;
  499. break;
  500. }
  501. SVG_PLOTTER* plotter = new SVG_PLOTTER();
  502. plotter->SetRenderSettings( aRenderSettings );
  503. plotter->SetPageSettings( plotPage );
  504. plotter->SetColorMode( aPlotOpts.m_blackAndWhite ? false : true );
  505. VECTOR2I plot_offset;
  506. double scalex = (double) plotPage.GetWidthMils() / actualPage.GetWidthMils();
  507. double scaley = (double) plotPage.GetHeightMils() / actualPage.GetHeightMils();
  508. double scale = std::min( scalex, scaley );
  509. // Currently, plot units are in decimil
  510. plotter->SetViewport( plot_offset, schIUScale.IU_PER_MILS / 10, scale, false );
  511. // Init :
  512. plotter->SetCreator( wxT( "Eeschema-SVG" ) );
  513. if( !plotter->OpenFile( aFileName ) )
  514. {
  515. delete plotter;
  516. return false;
  517. }
  518. LOCALE_IO toggle;
  519. plotter->StartPlot( m_schematic->CurrentSheet().GetPageNumber() );
  520. if( aPlotOpts.m_useBackgroundColor && plotter->GetColorMode() )
  521. {
  522. plotter->SetColor( plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_BACKGROUND ) );
  523. // Use page size selected in schematic to know the schematic bg area
  524. VECTOR2I end( actualPage.GetWidthIU( schIUScale.IU_PER_MILS ),
  525. actualPage.GetHeightIU( schIUScale.IU_PER_MILS ) );
  526. plotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 );
  527. }
  528. if( aPlotOpts.m_plotDrawingSheet )
  529. {
  530. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  531. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  532. COLOR4D color = plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET );
  533. PlotDrawingSheet( plotter, &aScreen->Schematic()->Prj(),
  534. aScreen->GetTitleBlock(),
  535. actualPage, aScreen->Schematic()->GetProperties(),
  536. aScreen->GetPageNumber(),
  537. aScreen->GetPageCount(), sheetName, sheetPath, aScreen->GetFileName(),
  538. plotter->GetColorMode() ? color : COLOR4D::BLACK,
  539. aScreen->GetVirtualPageNumber() == 1 );
  540. }
  541. aScreen->Plot( plotter, aPlotOpts );
  542. plotter->EndPlot();
  543. delete plotter;
  544. return true;
  545. }
  546. void SCH_PLOTTER::createDXFFiles( const SCH_PLOT_OPTS& aPlotOpts,
  547. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  548. {
  549. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet();
  550. /* When printing all pages, the printed page is not the current page. In complex hierarchies,
  551. * we must update symbol references and other parameters in the given printed SCH_SCREEN,
  552. * according to the sheet path because in complex hierarchies a SCH_SCREEN (a drawing ) is
  553. * shared between many sheets and symbol references depend on the actual sheet path used.
  554. */
  555. SCH_SHEET_LIST sheetList;
  556. if( aPlotOpts.m_plotAll )
  557. {
  558. sheetList.BuildSheetList( &m_schematic->Root(), true );
  559. sheetList.SortByPageNumbers();
  560. // remove the non-selected pages if we are in plot pages mode
  561. if( aPlotOpts.m_plotPages.size() > 0 )
  562. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  563. }
  564. else
  565. {
  566. // in Eeschema, this prints the current page
  567. sheetList.push_back( m_schematic->CurrentSheet() );
  568. }
  569. for( unsigned i = 0; i < sheetList.size(); i++ )
  570. {
  571. m_schematic->SetCurrentSheet( sheetList[i] );
  572. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  573. m_schematic->SetSheetNumberAndCount();
  574. SCH_SCREEN* screen = m_schematic->CurrentSheet().LastScreen();
  575. VECTOR2I plot_offset;
  576. wxString msg;
  577. try
  578. {
  579. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  580. // The sub sheet can be in a sub_hierarchy, but we plot the file in the
  581. // main project folder (or the folder specified by the caller),
  582. // so replace separators to create a unique filename:
  583. fname.Replace( "/", "_" );
  584. fname.Replace( "\\", "_" );
  585. wxString ext = DXF_PLOTTER::GetDefaultFileExtension();
  586. wxFileName plotFileName = createPlotFileName( aPlotOpts, fname, ext, aReporter );
  587. m_lastOutputFilePath = plotFileName.GetFullPath();
  588. if( !plotFileName.IsOk() )
  589. return;
  590. if( plotOneSheetDXF( plotFileName.GetFullPath(), screen, aRenderSettings, plot_offset,
  591. 1.0, aPlotOpts ) )
  592. {
  593. if( aReporter )
  594. {
  595. msg.Printf( _( "Plotted to '%s'." ), plotFileName.GetFullPath() );
  596. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  597. }
  598. }
  599. else // Error
  600. {
  601. if( aReporter )
  602. {
  603. msg.Printf( _( "Failed to create file '%s'." ), plotFileName.GetFullPath() );
  604. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  605. }
  606. }
  607. }
  608. catch( IO_ERROR& e )
  609. {
  610. if( aReporter )
  611. {
  612. msg.Printf( wxT( "DXF Plotter exception: %s" ), e.What() );
  613. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  614. }
  615. m_schematic->SetCurrentSheet( oldsheetpath );
  616. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  617. m_schematic->SetSheetNumberAndCount();
  618. return;
  619. }
  620. }
  621. if( aReporter )
  622. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  623. restoreEnvironment( nullptr, oldsheetpath );
  624. }
  625. bool SCH_PLOTTER::plotOneSheetDXF( const wxString& aFileName, SCH_SCREEN* aScreen,
  626. RENDER_SETTINGS* aRenderSettings, const VECTOR2I& aPlotOffset,
  627. double aScale, const SCH_PLOT_OPTS& aPlotOpts )
  628. {
  629. aRenderSettings->LoadColors( m_colorSettings );
  630. aRenderSettings->SetDefaultPenWidth( 0 );
  631. const PAGE_INFO& pageInfo = aScreen->GetPageSettings();
  632. DXF_PLOTTER* plotter = new DXF_PLOTTER();
  633. plotter->SetRenderSettings( aRenderSettings );
  634. plotter->SetPageSettings( pageInfo );
  635. plotter->SetColorMode( !aPlotOpts.m_blackAndWhite );
  636. // Currently, plot units are in decimil
  637. plotter->SetViewport( aPlotOffset, schIUScale.IU_PER_MILS / 10, aScale, false );
  638. // Init :
  639. plotter->SetCreator( wxT( "Eeschema-DXF" ) );
  640. if( !plotter->OpenFile( aFileName ) )
  641. {
  642. delete plotter;
  643. return false;
  644. }
  645. LOCALE_IO toggle;
  646. plotter->StartPlot( m_schematic->CurrentSheet().GetPageNumber() );
  647. if( aPlotOpts.m_plotDrawingSheet )
  648. {
  649. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  650. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  651. COLOR4D color = plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET );
  652. PlotDrawingSheet( plotter, &m_schematic->Prj(),
  653. aScreen->GetTitleBlock(),
  654. pageInfo,
  655. aScreen->Schematic()->GetProperties(), aScreen->GetPageNumber(),
  656. aScreen->GetPageCount(), sheetName, sheetPath, aScreen->GetFileName(),
  657. plotter->GetColorMode() ? color : COLOR4D::BLACK,
  658. aScreen->GetVirtualPageNumber() == 1 );
  659. }
  660. aScreen->Plot( plotter, aPlotOpts );
  661. // finish
  662. plotter->EndPlot();
  663. delete plotter;
  664. return true;
  665. }
  666. void SCH_PLOTTER::restoreEnvironment( PDF_PLOTTER* aPlotter, SCH_SHEET_PATH& aOldsheetpath )
  667. {
  668. if( aPlotter )
  669. {
  670. aPlotter->EndPlot();
  671. delete aPlotter;
  672. }
  673. // Restore the initial sheet
  674. m_schematic->SetCurrentSheet( aOldsheetpath );
  675. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  676. m_schematic->SetSheetNumberAndCount();
  677. }
  678. wxFileName SCH_PLOTTER::createPlotFileName( const SCH_PLOT_OPTS& aPlotOpts,
  679. const wxString& aPlotFileName,
  680. const wxString& aExtension, REPORTER* aReporter )
  681. {
  682. wxFileName retv;
  683. wxFileName tmp;
  684. tmp.SetPath( aPlotOpts.m_outputDirectory );
  685. retv.SetPath( tmp.GetPath() );
  686. if( !aPlotFileName.IsEmpty() )
  687. retv.SetName( aPlotFileName );
  688. else
  689. retv.SetName( _( "Schematic" ) );
  690. retv.SetExt( aExtension );
  691. if( !EnsureFileDirectoryExists( &tmp, retv.GetFullName(), aReporter ) || !tmp.IsDirWritable() )
  692. {
  693. if( aReporter )
  694. {
  695. wxString msg = wxString::Format( _( "Failed to write plot files to folder '%s'." ),
  696. tmp.GetPath() );
  697. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  698. }
  699. retv.Clear();
  700. SCHEMATIC_SETTINGS& settings = m_schematic->Settings();
  701. settings.m_PlotDirectoryName.Clear();
  702. }
  703. else
  704. {
  705. retv.SetPath( tmp.GetPath() );
  706. }
  707. wxLogTrace( tracePathsAndFiles, "Writing plot file '%s'.", retv.GetFullPath() );
  708. return retv;
  709. }
  710. void SCH_PLOTTER::Plot( PLOT_FORMAT aPlotFormat, const SCH_PLOT_OPTS& aPlotOpts,
  711. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  712. {
  713. SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
  714. m_colorSettings = settingsMgr.GetColorSettings( aPlotOpts.m_theme );
  715. switch( aPlotFormat )
  716. {
  717. default:
  718. case PLOT_FORMAT::POST: createPSFiles( aPlotOpts, aRenderSettings, aReporter ); break;
  719. case PLOT_FORMAT::DXF: createDXFFiles( aPlotOpts, aRenderSettings, aReporter ); break;
  720. case PLOT_FORMAT::PDF: createPDFFile( aPlotOpts, aRenderSettings, aReporter ); break;
  721. case PLOT_FORMAT::SVG: createSVGFiles( aPlotOpts, aRenderSettings, aReporter ); break;
  722. case PLOT_FORMAT::HPGL: /* no longer supported */ break;
  723. }
  724. }