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.

384 lines
14 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2023 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include "sch_printout.h"
  21. #include <tool/tool_manager.h>
  22. #include <tools/ee_selection_tool.h>
  23. #include <sch_edit_frame.h>
  24. #include <math/vector2wx.h>
  25. #include <pgm_base.h>
  26. #include <settings/color_settings.h>
  27. #include <settings/settings_manager.h>
  28. #include <sch_painter.h>
  29. #include <view/view.h>
  30. #include <gal/gal_print.h>
  31. #include <gal/graphics_abstraction_layer.h>
  32. #include <gal/painter.h>
  33. #include <zoom_defines.h>
  34. SCH_PRINTOUT::SCH_PRINTOUT( SCH_EDIT_FRAME* aParent, const wxString& aTitle, bool aUseCairo ) :
  35. wxPrintout( aTitle )
  36. {
  37. wxASSERT( aParent != nullptr );
  38. m_parent = aParent;
  39. m_useCairo = aUseCairo;
  40. m_view = nullptr;
  41. }
  42. void SCH_PRINTOUT::GetPageInfo( int* minPage, int* maxPage, int* selPageFrom, int* selPageTo )
  43. {
  44. *minPage = *selPageFrom = 1;
  45. *maxPage = *selPageTo = m_parent->Schematic().Root().CountSheets();
  46. }
  47. bool SCH_PRINTOUT::HasPage( int pageNum )
  48. {
  49. return m_parent->Schematic().Root().CountSheets() >= pageNum;
  50. }
  51. bool SCH_PRINTOUT::OnBeginDocument( int startPage, int endPage )
  52. {
  53. if( !wxPrintout::OnBeginDocument( startPage, endPage ) )
  54. return false;
  55. return true;
  56. }
  57. bool SCH_PRINTOUT::OnPrintPage( int page )
  58. {
  59. SCH_SHEET_LIST sheetList = m_parent->Schematic().BuildSheetListSortedByPageNumbers();
  60. wxCHECK_MSG( page >= 1 && page <= (int)sheetList.size(), false,
  61. wxT( "Cannot print invalid page number." ) );
  62. wxCHECK_MSG( sheetList[ page - 1].LastScreen() != nullptr, false,
  63. wxT( "Cannot print page with NULL screen." ) );
  64. wxString msg;
  65. msg.Printf( _( "Print page %d" ), page );
  66. m_parent->SetMsgPanel( msg, wxEmptyString );
  67. SCH_SCREEN* screen = m_parent->GetScreen();
  68. SCH_SHEET_PATH oldsheetpath = m_parent->GetCurrentSheet();
  69. m_parent->SetCurrentSheet( sheetList[ page - 1 ] );
  70. m_parent->GetCurrentSheet().UpdateAllScreenReferences();
  71. m_parent->SetSheetNumberAndCount();
  72. m_parent->RecomputeIntersheetRefs();
  73. screen = m_parent->GetCurrentSheet().LastScreen();
  74. PrintPage( screen );
  75. m_parent->SetCurrentSheet( oldsheetpath );
  76. m_parent->GetCurrentSheet().UpdateAllScreenReferences();
  77. m_parent->SetSheetNumberAndCount();
  78. return true;
  79. }
  80. int SCH_PRINTOUT::milsToIU( int aMils )
  81. {
  82. return KiROUND( aMils * schIUScale.IU_PER_MILS );
  83. }
  84. /*
  85. * This is the real print function: print the active screen
  86. */
  87. void SCH_PRINTOUT::PrintPage( SCH_SCREEN* aScreen )
  88. {
  89. if( !m_useCairo )
  90. {
  91. // Version using print to a wxDC
  92. // Warning:
  93. // When printing many pages, changes in the current wxDC will affect all next printings
  94. // because all prints are using the same wxPrinterDC after creation
  95. // So be careful and reinit parameters, especially when using offsets.
  96. VECTOR2I tmp_startvisu;
  97. wxSize pageSizeIU; // Page size in internal units
  98. VECTOR2I old_org;
  99. wxRect fitRect;
  100. wxDC* dc = GetDC();
  101. wxBusyCursor dummy;
  102. // Save current offsets and clip box.
  103. tmp_startvisu = aScreen->m_StartVisu;
  104. old_org = aScreen->m_DrawOrg;
  105. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  106. EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
  107. COLOR_SETTINGS* theme = mgr.GetColorSettings( cfg->m_Printing.color_theme );
  108. // Change scale factor and offset to print the whole page.
  109. bool printDrawingSheet = cfg->m_Printing.title_block;
  110. pageSizeIU = ToWxSize( aScreen->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS ) );
  111. FitThisSizeToPaper( pageSizeIU );
  112. fitRect = GetLogicalPaperRect();
  113. // When is the actual paper size does not match the schematic page size, the drawing will
  114. // not be centered on X or Y axis. Give a draw offset to center the schematic page on the
  115. // paper draw area.
  116. int xoffset = ( fitRect.width - pageSizeIU.x ) / 2;
  117. int yoffset = ( fitRect.height - pageSizeIU.y ) / 2;
  118. // Using a wxAffineMatrix2D has a big advantage: it handles different pages orientations
  119. //(PORTRAIT/LANDSCAPE), but the affine matrix is not always supported
  120. if( dc->CanUseTransformMatrix() )
  121. {
  122. wxAffineMatrix2D matrix; // starts from a unity matrix (the current wxDC default)
  123. // Check for portrait/landscape mismatch:
  124. if( ( fitRect.width > fitRect.height ) != ( pageSizeIU.x > pageSizeIU.y ) )
  125. {
  126. // Rotate the coordinates, and keep the draw coordinates inside the page
  127. matrix.Rotate( M_PI_2 );
  128. matrix.Translate( 0, -pageSizeIU.y );
  129. // Recalculate the offsets and page sizes according to the page rotation
  130. std::swap( pageSizeIU.x, pageSizeIU.y );
  131. FitThisSizeToPaper( pageSizeIU );
  132. fitRect = GetLogicalPaperRect();
  133. xoffset = ( fitRect.width - pageSizeIU.x ) / 2;
  134. yoffset = ( fitRect.height - pageSizeIU.y ) / 2;
  135. // All the coordinates will be rotated 90 deg when printing,
  136. // so the X,Y offset vector must be rotated -90 deg before printing
  137. std::swap( xoffset, yoffset );
  138. std::swap( fitRect.width, fitRect.height );
  139. yoffset = -yoffset;
  140. }
  141. matrix.Translate( xoffset, yoffset );
  142. dc->SetTransformMatrix( matrix );
  143. fitRect.x -= xoffset;
  144. fitRect.y -= yoffset;
  145. }
  146. else
  147. {
  148. SetLogicalOrigin( 0, 0 ); // Reset all offset settings made previously.
  149. // When printing previous pages (all prints are using the same wxDC)
  150. OffsetLogicalOrigin( xoffset, yoffset );
  151. }
  152. dc->SetLogicalFunction( wxCOPY );
  153. GRResetPenAndBrush( dc );
  154. COLOR4D savedBgColor = m_parent->GetDrawBgColor();
  155. COLOR4D bgColor = m_parent->GetColorSettings()->GetColor( LAYER_SCHEMATIC_BACKGROUND );
  156. if( cfg->m_Printing.background )
  157. {
  158. if( cfg->m_Printing.use_theme && theme )
  159. bgColor = theme->GetColor( LAYER_SCHEMATIC_BACKGROUND );
  160. }
  161. else
  162. {
  163. bgColor = COLOR4D::WHITE;
  164. }
  165. m_parent->SetDrawBgColor( bgColor );
  166. GRSFilledRect( dc, fitRect.GetX(), fitRect.GetY(), fitRect.GetRight(), fitRect.GetBottom(), 0,
  167. bgColor, bgColor );
  168. if( cfg->m_Printing.monochrome )
  169. GRForceBlackPen( true );
  170. SCH_RENDER_SETTINGS renderSettings( *m_parent->GetRenderSettings() );
  171. renderSettings.SetPrintDC( dc );
  172. if( cfg->m_Printing.use_theme && theme )
  173. renderSettings.LoadColors( theme );
  174. renderSettings.SetBackgroundColor( bgColor );
  175. // The drawing-sheet-item print code is shared between PCBNew and Eeschema, so it's easier
  176. // if they just use the PCB layer.
  177. renderSettings.SetLayerColor( LAYER_DRAWINGSHEET,
  178. renderSettings.GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET ) );
  179. renderSettings.SetDefaultFont( cfg->m_Appearance.default_font );
  180. if( printDrawingSheet )
  181. {
  182. m_parent->PrintDrawingSheet( &renderSettings, aScreen, aScreen->Schematic()->GetProperties(),
  183. schIUScale.IU_PER_MILS, aScreen->GetFileName(), wxEmptyString );
  184. }
  185. renderSettings.SetIsPrinting( true );
  186. aScreen->Print( &renderSettings );
  187. m_parent->SetDrawBgColor( savedBgColor );
  188. GRForceBlackPen( false );
  189. aScreen->m_StartVisu = tmp_startvisu;
  190. aScreen->m_DrawOrg = old_org;
  191. }
  192. else
  193. {
  194. wxDC* dc = GetDC();
  195. m_view = m_parent->GetCanvas()->GetView();
  196. KIGFX::GAL_DISPLAY_OPTIONS options;
  197. options.cairo_antialiasing_mode = KIGFX::CAIRO_ANTIALIASING_MODE::GOOD;
  198. std::unique_ptr<KIGFX::GAL_PRINT> galPrint = KIGFX::GAL_PRINT::Create( options, dc );
  199. KIGFX::GAL* gal = galPrint->GetGAL();
  200. KIGFX::PRINT_CONTEXT* printCtx = galPrint->GetPrintCtx();
  201. std::unique_ptr<KIGFX::SCH_PAINTER> painter = std::make_unique<KIGFX::SCH_PAINTER>( gal );
  202. std::unique_ptr<KIGFX::VIEW> view( m_view->DataReference() );
  203. painter->SetSchematic( &m_parent->Schematic() );
  204. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  205. EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
  206. COLOR_SETTINGS* theme = mgr.GetColorSettings( cfg->m_Printing.color_theme );
  207. EE_SELECTION_TOOL* selTool = m_parent->GetToolManager()->GetTool<EE_SELECTION_TOOL>();
  208. // Target paper size
  209. wxRect pageSizePx = GetLogicalPageRect();
  210. const VECTOR2D pageSizeIn( (double) pageSizePx.width / dc->GetPPI().x,
  211. (double) pageSizePx.height / dc->GetPPI().y );
  212. const VECTOR2D pageSizeIU( milsToIU( pageSizeIn.x * 1000 ), milsToIU( pageSizeIn.y * 1000 ) );
  213. galPrint->SetSheetSize( pageSizeIn );
  214. view->SetGAL( gal );
  215. view->SetPainter( painter.get() );
  216. view->SetScaleLimits( ZOOM_MAX_LIMIT_EESCHEMA, ZOOM_MIN_LIMIT_EESCHEMA );
  217. view->SetScale( 1.0 );
  218. gal->SetWorldUnitLength( SCH_WORLD_UNIT );
  219. // Init the SCH_RENDER_SETTINGS used by the painter used to print schematic
  220. SCH_RENDER_SETTINGS* dstSettings = painter->GetSettings();
  221. dstSettings->m_ShowPinsElectricalType = false;
  222. // Set the color scheme
  223. dstSettings->LoadColors( m_parent->GetColorSettings( false ) );
  224. if( cfg->m_Printing.use_theme && theme )
  225. dstSettings->LoadColors( theme );
  226. bool printDrawingSheet = cfg->m_Printing.title_block;
  227. COLOR4D bgColor = m_parent->GetColorSettings()->GetColor( LAYER_SCHEMATIC_BACKGROUND );
  228. if( cfg->m_Printing.background )
  229. {
  230. if( cfg->m_Printing.use_theme && theme )
  231. bgColor = theme->GetColor( LAYER_SCHEMATIC_BACKGROUND );
  232. }
  233. else
  234. {
  235. bgColor = COLOR4D::WHITE;
  236. }
  237. dstSettings->SetBackgroundColor( bgColor );
  238. // The drawing-sheet-item print code is shared between PCBNew and Eeschema, so it's easier
  239. // if they just use the PCB layer.
  240. dstSettings->SetLayerColor( LAYER_DRAWINGSHEET,
  241. dstSettings->GetLayerColor( LAYER_SCHEMATIC_DRAWINGSHEET ) );
  242. dstSettings->SetDefaultFont( cfg->m_Appearance.default_font );
  243. if( cfg->m_Printing.monochrome )
  244. {
  245. for( int i = 0; i < LAYER_ID_COUNT; ++i )
  246. dstSettings->SetLayerColor( i, COLOR4D::BLACK );
  247. // In B&W mode, draw the background only in white, because any other color
  248. // will be replaced by a black background
  249. dstSettings->SetBackgroundColor( COLOR4D::WHITE );
  250. dstSettings->m_OverrideItemColors = true;
  251. // Disable print some backgrounds
  252. dstSettings->SetPrintBlackAndWhite( true );
  253. }
  254. else // color enabled
  255. {
  256. for( int i = 0; i < LAYER_ID_COUNT; ++i )
  257. {
  258. // Cairo does not support translucent colors on PostScript surfaces
  259. // see 'Features support by the PostScript surface' on
  260. // https://www.cairographics.org/documentation/using_the_postscript_surface/
  261. dstSettings->SetLayerColor( i, dstSettings->GetLayerColor( i ).WithAlpha( 1.0 ) );
  262. }
  263. }
  264. dstSettings->SetIsPrinting( true );
  265. VECTOR2I sheetSizeIU = aScreen->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
  266. BOX2I drawingAreaBBox = BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( sheetSizeIU ) );
  267. // Enable all layers and use KIGFX::TARGET_NONCACHED to force update drawings
  268. // for printing with current GAL instance
  269. for( int i = 0; i < KIGFX::VIEW::VIEW_MAX_LAYERS; ++i )
  270. {
  271. view->SetLayerVisible( i, true );
  272. view->SetLayerTarget( i, KIGFX::TARGET_NONCACHED );
  273. }
  274. view->SetLayerVisible( LAYER_DRAWINGSHEET, printDrawingSheet );
  275. // Don't draw the selection if it's not from the current screen
  276. for( EDA_ITEM* item : selTool->GetSelection() )
  277. {
  278. if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
  279. {
  280. if( !m_parent->GetScreen()->CheckIfOnDrawList( schItem ) )
  281. view->SetLayerVisible( LAYER_SELECT_OVERLAY, false );
  282. break;
  283. }
  284. }
  285. // When is the actual paper size does not match the schematic page size,
  286. // we need to adjust the print scale to fit the selected paper size (pageSizeIU)
  287. double scaleX = (double) pageSizeIU.x / drawingAreaBBox.GetWidth();
  288. double scaleY = (double) pageSizeIU.y / drawingAreaBBox.GetHeight();
  289. double print_scale = std::min( scaleX, scaleY );
  290. galPrint->SetNativePaperSize( pageSizeIn, printCtx->HasNativeLandscapeRotation() );
  291. gal->SetLookAtPoint( drawingAreaBBox.Centre() );
  292. gal->SetZoomFactor( print_scale );
  293. gal->SetClearColor( dstSettings->GetBackgroundColor() );
  294. gal->ClearScreen();
  295. // Needed to use the same order for printing as for screen redraw
  296. view->UseDrawPriority( true );
  297. {
  298. KIGFX::GAL_DRAWING_CONTEXT ctx( gal );
  299. view->Redraw();
  300. }
  301. }
  302. }