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.

1083 lines
37 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_hpgl.h>
  31. #include <plotters/plotter_dxf.h>
  32. #include <plotters/plotters_pslike.h>
  33. #include <pgm_base.h>
  34. #include <trace_helpers.h>
  35. #include <sch_edit_frame.h>
  36. #include <sch_painter.h>
  37. #include <schematic.h>
  38. #include <sch_screen.h>
  39. #include <settings/settings_manager.h>
  40. // Note:
  41. // We need to switch between sheets to plot a hierarchy and update references and sheet number
  42. // Use SCHEMATIC::SetCurrentSheet( xxx ) to switch to a sheet.
  43. // Do not use SCH_EDIT_FRAME::SetCurrentSheet( xxx ) to switch to a sheet, because the new sheet
  44. // is not displayed, but SCH_EDIT_FRAME::SetCurrentSheet() has side effects to the current VIEW
  45. // (clear some data used to show the sheet on screen) and does not fully restore the "old" screen
  46. static const wxChar* plot_sheet_list( HPGL_PAGE_SIZE aSize )
  47. {
  48. switch( aSize )
  49. {
  50. default:
  51. case HPGL_PAGE_SIZE::DEFAULT: return nullptr;
  52. case HPGL_PAGE_SIZE::SIZE_A5: return wxT( "A5" );
  53. case HPGL_PAGE_SIZE::SIZE_A4: return wxT( "A4" );
  54. case HPGL_PAGE_SIZE::SIZE_A3: return wxT( "A3" );
  55. case HPGL_PAGE_SIZE::SIZE_A2: return wxT( "A2" );
  56. case HPGL_PAGE_SIZE::SIZE_A1: return wxT( "A1" );
  57. case HPGL_PAGE_SIZE::SIZE_A0: return wxT( "A0" );
  58. case HPGL_PAGE_SIZE::SIZE_A: return wxT( "A" );
  59. case HPGL_PAGE_SIZE::SIZE_B: return wxT( "B" );
  60. case HPGL_PAGE_SIZE::SIZE_C: return wxT( "C" );
  61. case HPGL_PAGE_SIZE::SIZE_D: return wxT( "D" );
  62. case HPGL_PAGE_SIZE::SIZE_E: return wxT( "E" );
  63. }
  64. }
  65. SCH_PLOTTER::SCH_PLOTTER( SCHEMATIC* aSchematic ) :
  66. m_schematic( aSchematic )
  67. {
  68. m_colorSettings = nullptr;
  69. }
  70. SCH_PLOTTER::SCH_PLOTTER( SCH_EDIT_FRAME* aFrame ) :
  71. m_schematic( &aFrame->Schematic() )
  72. {
  73. m_colorSettings = nullptr;
  74. }
  75. wxFileName SCH_PLOTTER::getOutputFilenameSingle( const SCH_PLOT_OPTS& aPlotOpts,
  76. REPORTER* aReporter, const wxString& aExt )
  77. {
  78. if( !aPlotOpts.m_outputFile.empty() )
  79. {
  80. return aPlotOpts.m_outputFile;
  81. }
  82. else
  83. {
  84. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  85. // The sub sheet can be in a sub_hierarchy, but we plot the file in the main
  86. // project folder (or the folder specified by the caller), so replace separators
  87. // to create a unique filename:
  88. fname.Replace( "/", "_" );
  89. fname.Replace( "\\", "_" );
  90. return createPlotFileName( aPlotOpts, fname, aExt, aReporter );
  91. }
  92. }
  93. void SCH_PLOTTER::createPDFFile( const SCH_PLOT_OPTS& aPlotOpts,
  94. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  95. {
  96. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet(); // sheetpath is saved here
  97. /* When printing all pages, the printed page is not the current page. In complex hierarchies,
  98. * we must update symbol references and other parameters in the given printed SCH_SCREEN,
  99. * according to the sheet path because in complex hierarchies a SCH_SCREEN (a drawing ) is
  100. * shared between many sheets and symbol references depend on the actual sheet path used.
  101. */
  102. SCH_SHEET_LIST sheetList;
  103. if( aPlotOpts.m_plotAll || aPlotOpts.m_plotPages.size() > 0 )
  104. {
  105. sheetList.BuildSheetList( &m_schematic->Root(), true );
  106. sheetList.SortByPageNumbers();
  107. // remove the non-selected pages if we are in plot pages mode
  108. if( aPlotOpts.m_plotPages.size() > 0 )
  109. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  110. }
  111. else
  112. {
  113. // in eeschema, this prints the current page
  114. sheetList.push_back( m_schematic->CurrentSheet() );
  115. }
  116. if( sheetList.empty() )
  117. {
  118. if( aReporter )
  119. aReporter->Report( _( "No sheets to plot." ), RPT_SEVERITY_ERROR );
  120. return;
  121. }
  122. wxCHECK( m_schematic, /* void */ );
  123. // Allocate the plotter and set the job level parameter
  124. PDF_PLOTTER* plotter = new PDF_PLOTTER( &m_schematic->Prj() );
  125. plotter->SetRenderSettings( aRenderSettings );
  126. plotter->SetColorMode( !aPlotOpts.m_blackAndWhite );
  127. plotter->SetCreator( wxT( "Eeschema-PDF" ) );
  128. plotter->SetTitle( ExpandTextVars( m_schematic->RootScreen()->GetTitleBlock().GetTitle(),
  129. &m_schematic->Prj() ) );
  130. wxString msg;
  131. wxFileName plotFileName;
  132. LOCALE_IO toggle; // Switch the locale to standard C
  133. for( unsigned i = 0; i < sheetList.size(); i++ )
  134. {
  135. m_schematic->SetCurrentSheet( sheetList[i] );
  136. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  137. m_schematic->SetSheetNumberAndCount();
  138. SCH_SCREEN* screen = m_schematic->CurrentSheet().LastScreen();
  139. wxString sheetName = sheetList[i].Last()->GetFields()[SHEETNAME].GetShownText( false );
  140. if( aPlotOpts.m_PDFMetadata )
  141. {
  142. msg = wxS( "AUTHOR" );
  143. if( m_schematic->ResolveTextVar( &sheetList[i], &msg, 0 ) )
  144. plotter->SetAuthor( msg );
  145. msg = wxS( "SUBJECT" );
  146. if( m_schematic->ResolveTextVar( &sheetList[i], &msg, 0 ) )
  147. plotter->SetSubject( msg );
  148. }
  149. if( i == 0 )
  150. {
  151. try
  152. {
  153. wxString ext = PDF_PLOTTER::GetDefaultFileExtension();
  154. plotFileName = getOutputFilenameSingle( aPlotOpts, aReporter, ext );
  155. m_lastOutputFilePath = plotFileName.GetFullPath();
  156. if( !plotFileName.IsOk() )
  157. return;
  158. if( !plotter->OpenFile( plotFileName.GetFullPath() ) )
  159. {
  160. if( aReporter )
  161. {
  162. msg.Printf( _( "Failed to create file '%s'." ),
  163. plotFileName.GetFullPath() );
  164. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  165. }
  166. delete plotter;
  167. return;
  168. }
  169. // Open the plotter and do the first page
  170. setupPlotPagePDF( plotter, screen, aPlotOpts );
  171. plotter->StartPlot( sheetList[i].GetPageNumber(), sheetName );
  172. }
  173. catch( const IO_ERROR& e )
  174. {
  175. // Cannot plot PDF file
  176. if( aReporter )
  177. {
  178. msg.Printf( wxT( "PDF Plotter exception: %s" ), e.What() );
  179. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  180. }
  181. restoreEnvironment( plotter, oldsheetpath );
  182. return;
  183. }
  184. }
  185. else
  186. {
  187. /* For the following pages you need to close the (finished) page,
  188. * reconfigure, and then start a new one */
  189. plotter->ClosePage();
  190. setupPlotPagePDF( plotter, screen, aPlotOpts );
  191. plotter->StartPage( sheetList[i].GetPageNumber(), sheetName );
  192. }
  193. plotOneSheetPDF( plotter, screen, aPlotOpts );
  194. }
  195. // Everything done, close the plot and restore the environment
  196. if( aReporter )
  197. {
  198. msg.Printf( _( "Plotted to '%s'.\n" ), plotFileName.GetFullPath() );
  199. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  200. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  201. }
  202. restoreEnvironment( plotter, oldsheetpath );
  203. }
  204. void SCH_PLOTTER::plotOneSheetPDF( PLOTTER* aPlotter, SCH_SCREEN* aScreen,
  205. const SCH_PLOT_OPTS& aPlotOpts )
  206. {
  207. if( aPlotOpts.m_useBackgroundColor && aPlotter->GetColorMode() )
  208. {
  209. aPlotter->SetColor( aPlotter->RenderSettings()->GetBackgroundColor() );
  210. // Use page size selected in schematic to know the schematic bg area
  211. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  212. VECTOR2I end( actualPage.GetWidthIU( schIUScale.IU_PER_MILS ),
  213. actualPage.GetHeightIU( schIUScale.IU_PER_MILS ) );
  214. aPlotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 );
  215. }
  216. if( aPlotOpts.m_plotDrawingSheet )
  217. {
  218. COLOR4D color = COLOR4D::BLACK;
  219. if( aPlotter->GetColorMode() )
  220. color = aPlotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET );
  221. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  222. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  223. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  224. PlotDrawingSheet( aPlotter, &aScreen->Schematic()->Prj(),
  225. aScreen->GetTitleBlock(),
  226. actualPage,
  227. aScreen->Schematic()->GetProperties(),
  228. aScreen->GetPageNumber(), aScreen->GetPageCount(), sheetName, sheetPath,
  229. aScreen->GetFileName(), color, aScreen->GetVirtualPageNumber() == 1 );
  230. }
  231. aScreen->Plot( aPlotter, aPlotOpts );
  232. }
  233. void SCH_PLOTTER::setupPlotPagePDF( PLOTTER* aPlotter, SCH_SCREEN* aScreen,
  234. const SCH_PLOT_OPTS& aPlotOpts )
  235. {
  236. PAGE_INFO plotPage; // page size selected to plot
  237. // Considerations on page size and scaling requests
  238. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  239. switch( aPlotOpts.m_pageSizeSelect )
  240. {
  241. case PAGE_SIZE_A:
  242. plotPage.SetType( wxT( "A" ) );
  243. plotPage.SetPortrait( actualPage.IsPortrait() );
  244. break;
  245. case PAGE_SIZE_A4:
  246. plotPage.SetType( wxT( "A4" ) );
  247. plotPage.SetPortrait( actualPage.IsPortrait() );
  248. break;
  249. case PAGE_SIZE_AUTO:
  250. default:
  251. plotPage = actualPage;
  252. break;
  253. }
  254. double scalex = (double) plotPage.GetWidthMils() / actualPage.GetWidthMils();
  255. double scaley = (double) plotPage.GetHeightMils() / actualPage.GetHeightMils();
  256. double scale = std::min( scalex, scaley );
  257. aPlotter->SetPageSettings( plotPage );
  258. // Currently, plot units are in decimil
  259. aPlotter->SetViewport( VECTOR2I( 0, 0 ), schIUScale.IU_PER_MILS / 10, scale, false );
  260. }
  261. void SCH_PLOTTER::createPSFiles( const SCH_PLOT_OPTS& aPlotOpts,
  262. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  263. {
  264. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet(); // sheetpath is saved here
  265. PAGE_INFO plotPage; // page size selected to plot
  266. wxString msg;
  267. /* When printing all pages, the printed page is not the current page.
  268. * In complex hierarchies, we must update symbol references and other parameters in the
  269. * given printed SCH_SCREEN, accordant to the sheet path because in complex hierarchies
  270. * a SCH_SCREEN (a drawing ) is shared between many sheets and symbol references
  271. * depend on the actual sheet path used.
  272. */
  273. SCH_SHEET_LIST sheetList;
  274. if( aPlotOpts.m_plotAll )
  275. {
  276. sheetList.BuildSheetList( &m_schematic->Root(), true );
  277. sheetList.SortByPageNumbers();
  278. // remove the non-selected pages if we are in plot pages mode
  279. if( aPlotOpts.m_plotPages.size() > 0 )
  280. {
  281. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  282. }
  283. }
  284. else
  285. {
  286. sheetList.push_back( m_schematic->CurrentSheet() );
  287. }
  288. for( unsigned i = 0; i < sheetList.size(); i++ )
  289. {
  290. m_schematic->SetCurrentSheet( sheetList[i] );
  291. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  292. m_schematic->SetSheetNumberAndCount();
  293. SCH_SCREEN* screen = m_schematic->CurrentSheet().LastScreen();
  294. PAGE_INFO actualPage = screen->GetPageSettings();
  295. switch( aPlotOpts.m_pageSizeSelect )
  296. {
  297. case PAGE_SIZE_A:
  298. plotPage.SetType( wxT( "A" ) );
  299. plotPage.SetPortrait( actualPage.IsPortrait() );
  300. break;
  301. case PAGE_SIZE_A4:
  302. plotPage.SetType( wxT( "A4" ) );
  303. plotPage.SetPortrait( actualPage.IsPortrait() );
  304. break;
  305. case PAGE_SIZE_AUTO:
  306. default: plotPage = actualPage; break;
  307. }
  308. double scalex = (double) plotPage.GetWidthMils() / actualPage.GetWidthMils();
  309. double scaley = (double) plotPage.GetHeightMils() / actualPage.GetHeightMils();
  310. double scale = std::min( scalex, scaley );
  311. VECTOR2I plot_offset;
  312. try
  313. {
  314. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  315. // The sub sheet can be in a sub_hierarchy, but we plot the file in the
  316. // main project folder (or the folder specified by the caller),
  317. // so replace separators to create a unique filename:
  318. fname.Replace( "/", "_" );
  319. fname.Replace( "\\", "_" );
  320. wxString ext = PS_PLOTTER::GetDefaultFileExtension();
  321. wxFileName plotFileName = createPlotFileName( aPlotOpts, fname, ext, aReporter );
  322. m_lastOutputFilePath = plotFileName.GetFullPath();
  323. if( !plotFileName.IsOk() )
  324. return;
  325. if( plotOneSheetPS( plotFileName.GetFullPath(), screen, aRenderSettings, actualPage,
  326. plot_offset, scale, aPlotOpts ) )
  327. {
  328. if( aReporter )
  329. {
  330. msg.Printf( _( "Plotted to '%s'." ), plotFileName.GetFullPath() );
  331. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  332. }
  333. }
  334. else
  335. {
  336. if( aReporter )
  337. {
  338. // Error
  339. msg.Printf( _( "Failed to create file '%s'." ), plotFileName.GetFullPath() );
  340. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  341. }
  342. }
  343. }
  344. catch( IO_ERROR& e )
  345. {
  346. if( aReporter )
  347. {
  348. msg.Printf( wxT( "PS Plotter exception: %s" ), e.What() );
  349. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  350. }
  351. }
  352. }
  353. if( aReporter )
  354. {
  355. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  356. }
  357. restoreEnvironment( nullptr, oldsheetpath );
  358. }
  359. bool SCH_PLOTTER::plotOneSheetPS( const wxString& aFileName, SCH_SCREEN* aScreen,
  360. RENDER_SETTINGS* aRenderSettings, const PAGE_INFO& aPageInfo,
  361. const VECTOR2I& aPlot0ffset, double aScale,
  362. const SCH_PLOT_OPTS& aPlotOpts )
  363. {
  364. PS_PLOTTER* plotter = new PS_PLOTTER();
  365. plotter->SetRenderSettings( aRenderSettings );
  366. plotter->SetPageSettings( aPageInfo );
  367. plotter->SetColorMode( !aPlotOpts.m_blackAndWhite );
  368. // Currently, plot units are in decimil
  369. plotter->SetViewport( aPlot0ffset, schIUScale.IU_PER_MILS / 10, aScale, false );
  370. // Init :
  371. plotter->SetCreator( wxT( "Eeschema-PS" ) );
  372. if( !plotter->OpenFile( aFileName ) )
  373. {
  374. delete plotter;
  375. return false;
  376. }
  377. LOCALE_IO toggle; // Switch the locale to standard C
  378. plotter->StartPlot( m_schematic->CurrentSheet().GetPageNumber() );
  379. if( aPlotOpts.m_useBackgroundColor && plotter->GetColorMode() )
  380. {
  381. plotter->SetColor( plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_BACKGROUND ) );
  382. // Use page size selected in schematic to know the schematic bg area
  383. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  384. VECTOR2I end( actualPage.GetWidthIU( schIUScale.IU_PER_MILS ),
  385. actualPage.GetHeightIU( schIUScale.IU_PER_MILS ) );
  386. plotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 );
  387. }
  388. if( aPlotOpts.m_plotDrawingSheet )
  389. {
  390. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  391. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  392. COLOR4D color = plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET );
  393. PlotDrawingSheet( plotter, &aScreen->Schematic()->Prj(),
  394. aScreen->GetTitleBlock(),
  395. aPageInfo, aScreen->Schematic()->GetProperties(),
  396. aScreen->GetPageNumber(), aScreen->GetPageCount(), sheetName, sheetPath,
  397. aScreen->GetFileName(), plotter->GetColorMode() ? color : COLOR4D::BLACK,
  398. aScreen->GetVirtualPageNumber() == 1 );
  399. }
  400. aScreen->Plot( plotter, aPlotOpts );
  401. plotter->EndPlot();
  402. delete plotter;
  403. return true;
  404. }
  405. void SCH_PLOTTER::createSVGFiles( const SCH_PLOT_OPTS& aPlotOpts,
  406. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  407. {
  408. wxString msg;
  409. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet();
  410. SCH_SHEET_LIST sheetList;
  411. if( aPlotOpts.m_plotAll )
  412. {
  413. sheetList.BuildSheetList( &m_schematic->Root(), true );
  414. sheetList.SortByPageNumbers();
  415. // remove the non-selected pages if we are in plot pages mode
  416. if( aPlotOpts.m_plotPages.size() > 0 )
  417. {
  418. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  419. }
  420. }
  421. else
  422. {
  423. // in eeschema, this prints the current page
  424. sheetList.push_back( m_schematic->CurrentSheet() );
  425. }
  426. for( unsigned i = 0; i < sheetList.size(); i++ )
  427. {
  428. SCH_SCREEN* screen;
  429. m_schematic->SetCurrentSheet( sheetList[i] );
  430. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  431. m_schematic->SetSheetNumberAndCount();
  432. screen = m_schematic->CurrentSheet().LastScreen();
  433. try
  434. {
  435. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  436. // The sub sheet can be in a sub_hierarchy, but we plot the file in the
  437. // main project folder (or the folder specified by the caller),
  438. // so replace separators to create a unique filename:
  439. fname.Replace( "/", "_" );
  440. fname.Replace( "\\", "_" );
  441. wxString ext = SVG_PLOTTER::GetDefaultFileExtension();
  442. wxFileName plotFileName = createPlotFileName( aPlotOpts, fname, ext, aReporter );
  443. m_lastOutputFilePath = plotFileName.GetFullPath();
  444. if( !plotFileName.IsOk() )
  445. return;
  446. bool success = plotOneSheetSVG( plotFileName.GetFullPath(), screen, aRenderSettings,
  447. aPlotOpts );
  448. if( !success )
  449. {
  450. if( aReporter )
  451. {
  452. msg.Printf( _( "Failed to create file '%s'." ), plotFileName.GetFullPath() );
  453. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  454. }
  455. }
  456. else
  457. {
  458. if( aReporter )
  459. {
  460. msg.Printf( _( "Plotted to '%s'." ), plotFileName.GetFullPath() );
  461. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  462. }
  463. }
  464. }
  465. catch( const IO_ERROR& e )
  466. {
  467. if( aReporter )
  468. {
  469. // Cannot plot SVG file
  470. msg.Printf( wxT( "SVG Plotter exception: %s" ), e.What() );
  471. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  472. }
  473. break;
  474. }
  475. }
  476. if( aReporter )
  477. {
  478. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  479. }
  480. restoreEnvironment( nullptr, oldsheetpath );
  481. }
  482. bool SCH_PLOTTER::plotOneSheetSVG( const wxString& aFileName, SCH_SCREEN* aScreen,
  483. RENDER_SETTINGS* aRenderSettings,
  484. const SCH_PLOT_OPTS& aPlotOpts )
  485. {
  486. PAGE_INFO plotPage;
  487. // Adjust page size and scaling requests
  488. const PAGE_INFO& actualPage = aScreen->GetPageSettings(); // page size selected in schematic
  489. switch( aPlotOpts.m_pageSizeSelect )
  490. {
  491. case PAGE_SIZE_A:
  492. plotPage.SetType( wxT( "A" ) );
  493. plotPage.SetPortrait( actualPage.IsPortrait() );
  494. break;
  495. case PAGE_SIZE_A4:
  496. plotPage.SetType( wxT( "A4" ) );
  497. plotPage.SetPortrait( actualPage.IsPortrait() );
  498. break;
  499. case PAGE_SIZE_AUTO:
  500. default:
  501. plotPage = actualPage;
  502. break;
  503. }
  504. SVG_PLOTTER* plotter = new SVG_PLOTTER();
  505. plotter->SetRenderSettings( aRenderSettings );
  506. plotter->SetPageSettings( plotPage );
  507. plotter->SetColorMode( aPlotOpts.m_blackAndWhite ? false : true );
  508. VECTOR2I plot_offset;
  509. double scalex = (double) plotPage.GetWidthMils() / actualPage.GetWidthMils();
  510. double scaley = (double) plotPage.GetHeightMils() / actualPage.GetHeightMils();
  511. double scale = std::min( scalex, scaley );
  512. // Currently, plot units are in decimil
  513. plotter->SetViewport( plot_offset, schIUScale.IU_PER_MILS / 10, scale, false );
  514. // Init :
  515. plotter->SetCreator( wxT( "Eeschema-SVG" ) );
  516. if( !plotter->OpenFile( aFileName ) )
  517. {
  518. delete plotter;
  519. return false;
  520. }
  521. LOCALE_IO toggle;
  522. plotter->StartPlot( m_schematic->CurrentSheet().GetPageNumber() );
  523. if( aPlotOpts.m_useBackgroundColor && plotter->GetColorMode() )
  524. {
  525. plotter->SetColor( plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_BACKGROUND ) );
  526. // Use page size selected in schematic to know the schematic bg area
  527. VECTOR2I end( actualPage.GetWidthIU( schIUScale.IU_PER_MILS ),
  528. actualPage.GetHeightIU( schIUScale.IU_PER_MILS ) );
  529. plotter->Rect( VECTOR2I( 0, 0 ), end, FILL_T::FILLED_SHAPE, 1.0 );
  530. }
  531. if( aPlotOpts.m_plotDrawingSheet )
  532. {
  533. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  534. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  535. COLOR4D color = plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET );
  536. PlotDrawingSheet( plotter, &aScreen->Schematic()->Prj(),
  537. aScreen->GetTitleBlock(),
  538. actualPage, aScreen->Schematic()->GetProperties(), aScreen->GetPageNumber(),
  539. aScreen->GetPageCount(), sheetName, sheetPath, aScreen->GetFileName(),
  540. plotter->GetColorMode() ? color : COLOR4D::BLACK,
  541. aScreen->GetVirtualPageNumber() == 1 );
  542. }
  543. aScreen->Plot( plotter, aPlotOpts );
  544. plotter->EndPlot();
  545. delete plotter;
  546. return true;
  547. }
  548. void SCH_PLOTTER::createHPGLFiles( const SCH_PLOT_OPTS& aPlotOpts,
  549. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  550. {
  551. SCH_SCREEN* screen = m_schematic->RootScreen();
  552. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet();
  553. /* When printing all pages, the printed page is not the current page. In complex hierarchies,
  554. * we must update symbol references and other parameters in the given printed SCH_SCREEN,
  555. * according to the sheet path because in complex hierarchies a SCH_SCREEN (a drawing ) is
  556. * shared between many sheets and symbol references depend on the actual sheet path used.
  557. */
  558. SCH_SHEET_LIST sheetList;
  559. if( aPlotOpts.m_plotAll )
  560. {
  561. sheetList.BuildSheetList( &m_schematic->Root(), true );
  562. sheetList.SortByPageNumbers();
  563. // remove the non-selected pages if we are in plot pages mode
  564. if( aPlotOpts.m_plotPages.size() > 0 )
  565. {
  566. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  567. }
  568. }
  569. else
  570. {
  571. // in eeschema, this prints the current page
  572. sheetList.push_back( m_schematic->CurrentSheet() );
  573. }
  574. for( unsigned i = 0; i < sheetList.size(); i++ )
  575. {
  576. m_schematic->SetCurrentSheet( sheetList[i] );
  577. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  578. m_schematic->SetSheetNumberAndCount();
  579. screen = m_schematic->CurrentSheet().LastScreen();
  580. if( !screen ) // LastScreen() may return NULL
  581. screen = m_schematic->RootScreen();
  582. const PAGE_INFO& curPage = screen->GetPageSettings();
  583. PAGE_INFO plotPage = curPage;
  584. // if plotting on a page size other than curPage
  585. plotPage.SetType( plot_sheet_list( aPlotOpts.m_HPGLPaperSizeSelect ) );
  586. // Calculation of conversion scales.
  587. double plot_scale = (double) plotPage.GetWidthMils() / curPage.GetWidthMils();
  588. // Calculate offsets
  589. VECTOR2I plotOffset;
  590. wxString msg;
  591. if( aPlotOpts.m_HPGLPlotOrigin == HPGL_PLOT_ORIGIN_AND_UNITS::PLOTTER_CENTER )
  592. {
  593. plotOffset.x = plotPage.GetWidthIU( schIUScale.IU_PER_MILS ) / 2;
  594. plotOffset.y = -plotPage.GetHeightIU( schIUScale.IU_PER_MILS ) / 2;
  595. }
  596. try
  597. {
  598. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  599. // The sub sheet can be in a sub_hierarchy, but we plot the file in the
  600. // main project folder (or the folder specified by the caller),
  601. // so replace separators to create a unique filename:
  602. fname.Replace( "/", "_" );
  603. fname.Replace( "\\", "_" );
  604. wxString ext = HPGL_PLOTTER::GetDefaultFileExtension();
  605. wxFileName plotFileName = createPlotFileName( aPlotOpts, fname, ext, aReporter );
  606. if( !plotFileName.IsOk() )
  607. return;
  608. LOCALE_IO toggle;
  609. if( plotOneSheetHpgl( plotFileName.GetFullPath(), screen, curPage, aRenderSettings,
  610. plotOffset, plot_scale, aPlotOpts ) )
  611. {
  612. if( aReporter )
  613. {
  614. msg.Printf( _( "Plotted to '%s'." ), plotFileName.GetFullPath() );
  615. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  616. }
  617. }
  618. else
  619. {
  620. if( aReporter )
  621. {
  622. msg.Printf( _( "Failed to create file '%s'." ), plotFileName.GetFullPath() );
  623. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  624. }
  625. }
  626. }
  627. catch( IO_ERROR& e )
  628. {
  629. if( aReporter )
  630. {
  631. msg.Printf( wxT( "HPGL Plotter exception: %s" ), e.What() );
  632. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  633. }
  634. }
  635. }
  636. if( aReporter )
  637. {
  638. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  639. }
  640. restoreEnvironment( nullptr, oldsheetpath );
  641. }
  642. bool SCH_PLOTTER::plotOneSheetHpgl( const wxString& aFileName, SCH_SCREEN* aScreen,
  643. const PAGE_INFO& aPageInfo, RENDER_SETTINGS* aRenderSettings,
  644. const VECTOR2I& aPlot0ffset, double aScale,
  645. const SCH_PLOT_OPTS& aPlotOpts )
  646. {
  647. HPGL_PLOTTER* plotter = new HPGL_PLOTTER();
  648. // Currently, plot units are in decimil
  649. plotter->SetPageSettings( aPageInfo );
  650. plotter->SetRenderSettings( aRenderSettings );
  651. plotter->RenderSettings()->LoadColors( m_colorSettings );
  652. plotter->SetViewport( aPlot0ffset, schIUScale.IU_PER_MILS/10, aScale, false );
  653. // TODO this could be configurable
  654. plotter->SetTargetChordLength( schIUScale.mmToIU( 0.6 ) );
  655. switch( aPlotOpts.m_HPGLPlotOrigin )
  656. {
  657. case HPGL_PLOT_ORIGIN_AND_UNITS::PLOTTER_BOT_LEFT:
  658. case HPGL_PLOT_ORIGIN_AND_UNITS::PLOTTER_CENTER:
  659. default:
  660. plotter->SetUserCoords( false );
  661. break;
  662. case HPGL_PLOT_ORIGIN_AND_UNITS::USER_FIT_PAGE:
  663. plotter->SetUserCoords( true );
  664. plotter->SetUserCoordsFit( false );
  665. break;
  666. case HPGL_PLOT_ORIGIN_AND_UNITS::USER_FIT_CONTENT:
  667. plotter->SetUserCoords( true );
  668. plotter->SetUserCoordsFit( true );
  669. break;
  670. }
  671. // Init :
  672. plotter->SetCreator( wxT( "Eeschema-HPGL" ) );
  673. if( !plotter->OpenFile( aFileName ) )
  674. {
  675. delete plotter;
  676. return false;
  677. }
  678. LOCALE_IO toggle;
  679. // Pen num and pen speed are not initialized here.
  680. // Default HPGL driver values are used
  681. plotter->SetPenDiameter( aPlotOpts.m_HPGLPenSize );
  682. plotter->StartPlot( m_schematic->CurrentSheet().GetPageNumber() );
  683. if( aPlotOpts.m_plotDrawingSheet )
  684. {
  685. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  686. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  687. PlotDrawingSheet( plotter, &m_schematic->Prj(),
  688. aScreen->GetTitleBlock(),
  689. aPageInfo,
  690. aScreen->Schematic()->GetProperties(), aScreen->GetPageNumber(),
  691. aScreen->GetPageCount(), sheetName, sheetPath, aScreen->GetFileName(),
  692. COLOR4D::BLACK, aScreen->GetVirtualPageNumber() == 1 );
  693. }
  694. aScreen->Plot( plotter, aPlotOpts );
  695. plotter->EndPlot();
  696. delete plotter;
  697. return true;
  698. }
  699. void SCH_PLOTTER::createDXFFiles( const SCH_PLOT_OPTS& aPlotOpts,
  700. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  701. {
  702. SCH_SHEET_PATH oldsheetpath = m_schematic->CurrentSheet();
  703. /* When printing all pages, the printed page is not the current page. In complex hierarchies,
  704. * we must update symbol references and other parameters in the given printed SCH_SCREEN,
  705. * according to the sheet path because in complex hierarchies a SCH_SCREEN (a drawing ) is
  706. * shared between many sheets and symbol references depend on the actual sheet path used.
  707. */
  708. SCH_SHEET_LIST sheetList;
  709. if( aPlotOpts.m_plotAll )
  710. {
  711. sheetList.BuildSheetList( &m_schematic->Root(), true );
  712. sheetList.SortByPageNumbers();
  713. // remove the non-selected pages if we are in plot pages mode
  714. if( aPlotOpts.m_plotPages.size() > 0 )
  715. sheetList.TrimToPageNumbers( aPlotOpts.m_plotPages );
  716. }
  717. else
  718. {
  719. // in eeschema, this prints the current page
  720. sheetList.push_back( m_schematic->CurrentSheet() );
  721. }
  722. for( unsigned i = 0; i < sheetList.size(); i++ )
  723. {
  724. m_schematic->SetCurrentSheet( sheetList[i] );
  725. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  726. m_schematic->SetSheetNumberAndCount();
  727. SCH_SCREEN* screen = m_schematic->CurrentSheet().LastScreen();
  728. VECTOR2I plot_offset;
  729. wxString msg;
  730. try
  731. {
  732. wxString fname = m_schematic->GetUniqueFilenameForCurrentSheet();
  733. // The sub sheet can be in a sub_hierarchy, but we plot the file in the
  734. // main project folder (or the folder specified by the caller),
  735. // so replace separators to create a unique filename:
  736. fname.Replace( "/", "_" );
  737. fname.Replace( "\\", "_" );
  738. wxString ext = DXF_PLOTTER::GetDefaultFileExtension();
  739. wxFileName plotFileName = createPlotFileName( aPlotOpts, fname, ext, aReporter );
  740. m_lastOutputFilePath = plotFileName.GetFullPath();
  741. if( !plotFileName.IsOk() )
  742. return;
  743. if( plotOneSheetDXF( plotFileName.GetFullPath(), screen, aRenderSettings, plot_offset,
  744. 1.0, aPlotOpts ) )
  745. {
  746. if( aReporter )
  747. {
  748. msg.Printf( _( "Plotted to '%s'." ), plotFileName.GetFullPath() );
  749. aReporter->Report( msg, RPT_SEVERITY_ACTION );
  750. }
  751. }
  752. else // Error
  753. {
  754. if( aReporter )
  755. {
  756. msg.Printf( _( "Failed to create file '%s'." ), plotFileName.GetFullPath() );
  757. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  758. }
  759. }
  760. }
  761. catch( IO_ERROR& e )
  762. {
  763. if( aReporter )
  764. {
  765. msg.Printf( wxT( "DXF Plotter exception: %s" ), e.What() );
  766. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  767. }
  768. m_schematic->SetCurrentSheet( oldsheetpath );
  769. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  770. m_schematic->SetSheetNumberAndCount();
  771. return;
  772. }
  773. }
  774. if( aReporter )
  775. aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
  776. restoreEnvironment( nullptr, oldsheetpath );
  777. }
  778. bool SCH_PLOTTER::plotOneSheetDXF( const wxString& aFileName, SCH_SCREEN* aScreen,
  779. RENDER_SETTINGS* aRenderSettings, const VECTOR2I& aPlotOffset,
  780. double aScale, const SCH_PLOT_OPTS& aPlotOpts )
  781. {
  782. aRenderSettings->LoadColors( m_colorSettings );
  783. aRenderSettings->SetDefaultPenWidth( 0 );
  784. const PAGE_INFO& pageInfo = aScreen->GetPageSettings();
  785. DXF_PLOTTER* plotter = new DXF_PLOTTER();
  786. plotter->SetRenderSettings( aRenderSettings );
  787. plotter->SetPageSettings( pageInfo );
  788. plotter->SetColorMode( !aPlotOpts.m_blackAndWhite );
  789. // Currently, plot units are in decimil
  790. plotter->SetViewport( aPlotOffset, schIUScale.IU_PER_MILS / 10, aScale, false );
  791. // Init :
  792. plotter->SetCreator( wxT( "Eeschema-DXF" ) );
  793. if( !plotter->OpenFile( aFileName ) )
  794. {
  795. delete plotter;
  796. return false;
  797. }
  798. LOCALE_IO toggle;
  799. plotter->StartPlot( m_schematic->CurrentSheet().GetPageNumber() );
  800. if( aPlotOpts.m_plotDrawingSheet )
  801. {
  802. wxString sheetName = m_schematic->CurrentSheet().Last()->GetName();
  803. wxString sheetPath = m_schematic->CurrentSheet().PathHumanReadable();
  804. COLOR4D color = plotter->RenderSettings()->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET );
  805. PlotDrawingSheet( plotter, &m_schematic->Prj(),
  806. aScreen->GetTitleBlock(),
  807. pageInfo,
  808. aScreen->Schematic()->GetProperties(), aScreen->GetPageNumber(),
  809. aScreen->GetPageCount(), sheetName, sheetPath, aScreen->GetFileName(),
  810. plotter->GetColorMode() ? color : COLOR4D::BLACK,
  811. aScreen->GetVirtualPageNumber() == 1 );
  812. }
  813. aScreen->Plot( plotter, aPlotOpts );
  814. // finish
  815. plotter->EndPlot();
  816. delete plotter;
  817. return true;
  818. }
  819. void SCH_PLOTTER::restoreEnvironment( PDF_PLOTTER* aPlotter, SCH_SHEET_PATH& aOldsheetpath )
  820. {
  821. if( aPlotter )
  822. {
  823. aPlotter->EndPlot();
  824. delete aPlotter;
  825. }
  826. // Restore the initial sheet
  827. m_schematic->SetCurrentSheet( aOldsheetpath );
  828. m_schematic->CurrentSheet().UpdateAllScreenReferences();
  829. m_schematic->SetSheetNumberAndCount();
  830. }
  831. wxFileName SCH_PLOTTER::createPlotFileName( const SCH_PLOT_OPTS& aPlotOpts,
  832. const wxString& aPlotFileName,
  833. const wxString& aExtension, REPORTER* aReporter )
  834. {
  835. wxFileName retv;
  836. wxFileName tmp;
  837. tmp.SetPath( aPlotOpts.m_outputDirectory );
  838. retv.SetPath( tmp.GetPath() );
  839. if( !aPlotFileName.IsEmpty() )
  840. retv.SetName( aPlotFileName );
  841. else
  842. retv.SetName( _( "Schematic" ) );
  843. retv.SetExt( aExtension );
  844. if( !EnsureFileDirectoryExists( &tmp, retv.GetFullName(), aReporter ) || !tmp.IsDirWritable() )
  845. {
  846. if( aReporter )
  847. {
  848. wxString msg = wxString::Format( _( "Failed to write plot files to folder '%s'." ),
  849. tmp.GetPath() );
  850. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  851. }
  852. retv.Clear();
  853. SCHEMATIC_SETTINGS& settings = m_schematic->Settings();
  854. settings.m_PlotDirectoryName.Clear();
  855. }
  856. else
  857. {
  858. retv.SetPath( tmp.GetPath() );
  859. }
  860. wxLogTrace( tracePathsAndFiles, "Writing plot file '%s'.", retv.GetFullPath() );
  861. return retv;
  862. }
  863. void SCH_PLOTTER::Plot( PLOT_FORMAT aPlotFormat, const SCH_PLOT_OPTS& aPlotOpts,
  864. SCH_RENDER_SETTINGS* aRenderSettings, REPORTER* aReporter )
  865. {
  866. SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
  867. m_colorSettings = settingsMgr.GetColorSettings( aPlotOpts.m_theme );
  868. switch( aPlotFormat )
  869. {
  870. default:
  871. case PLOT_FORMAT::POST: createPSFiles( aPlotOpts, aRenderSettings, aReporter ); break;
  872. case PLOT_FORMAT::DXF: createDXFFiles( aPlotOpts, aRenderSettings, aReporter ); break;
  873. case PLOT_FORMAT::PDF: createPDFFile( aPlotOpts, aRenderSettings, aReporter ); break;
  874. case PLOT_FORMAT::SVG: createSVGFiles( aPlotOpts, aRenderSettings, aReporter ); break;
  875. case PLOT_FORMAT::HPGL: createHPGLFiles( aPlotOpts, aRenderSettings, aReporter ); break;
  876. }
  877. }