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.

461 lines
14 KiB

  1. /**
  2. * @file page_layout_writer.cpp
  3. * @brief write an S expression of description of graphic items and texts
  4. * to build a title block and page layout
  5. */
  6. /*
  7. * This program source code file is part of KiCad, a free EDA CAD application.
  8. *
  9. * Copyright (C) 2013-2016 CERN
  10. * Copyright (C) 2019 Kicad Developers, see AUTHORS.txt for contributors.
  11. *
  12. * @author Jean-Pierre Charras, jp.charras at wanadoo.fr
  13. *
  14. * This program is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU General Public License
  16. * as published by the Free Software Foundation; either version 2
  17. * of the License, or (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program; if not, you may find one here:
  26. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  27. * or you may search the http://www.gnu.org website for the version 2 license,
  28. * or you may write to the Free Software Foundation, Inc.,
  29. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  30. */
  31. #include <fctsys.h>
  32. #include <base_struct.h>
  33. #include <ws_painter.h>
  34. #include <ws_draw_item.h>
  35. #include <ws_data_model.h>
  36. #include <math/vector2d.h>
  37. #include <page_layout_reader_lexer.h>
  38. #include <macros.h>
  39. #include <convert_to_biu.h>
  40. using namespace TB_READER_T;
  41. #define double2Str Double2Str
  42. // A helper function to write tokens:
  43. static const char* getTokenName( T aTok )
  44. {
  45. return PAGE_LAYOUT_READER_LEXER::TokenName( aTok );
  46. }
  47. // A basic helper class to write a page layout description
  48. // Not used alone, a file writer or a string writer should be
  49. // derived to use it
  50. // Therefore the constructor is protected
  51. class WS_DATA_MODEL_IO
  52. {
  53. protected:
  54. OUTPUTFORMATTER* m_out;
  55. WS_DATA_MODEL_IO() { m_out = NULL; }
  56. virtual ~WS_DATA_MODEL_IO() {}
  57. public:
  58. void Format( WS_DATA_MODEL* aModel ) const;
  59. void Format( WS_DATA_MODEL* aModel, WS_DATA_ITEM* aItem, int aNestLevel ) const;
  60. private:
  61. void format( WS_DATA_ITEM_TEXT* aItem, int aNestLevel ) const;
  62. void format( WS_DATA_MODEL* aModel, WS_DATA_ITEM* aItem, int aNestLevel ) const;
  63. void format( WS_DATA_ITEM_POLYGONS* aItem, int aNestLevel )
  64. const;
  65. void format( WS_DATA_ITEM_BITMAP* aItem, int aNestLevel ) const;
  66. void formatCoordinate( const char * aToken, POINT_COORD & aCoord ) const;
  67. void formatRepeatParameters( WS_DATA_ITEM* aItem ) const;
  68. void formatOptions( WS_DATA_ITEM* aItem ) const;
  69. };
  70. // A helper class to write a page layout description to a file
  71. class WS_DATA_MODEL_FILEIO: public WS_DATA_MODEL_IO
  72. {
  73. FILE_OUTPUTFORMATTER * m_fileout;
  74. public:
  75. WS_DATA_MODEL_FILEIO( const wxString& aFilename ):
  76. WS_DATA_MODEL_IO()
  77. {
  78. try
  79. {
  80. m_fileout = new FILE_OUTPUTFORMATTER( aFilename );
  81. m_out = m_fileout;
  82. }
  83. catch( const IO_ERROR& ioe )
  84. {
  85. wxMessageBox( ioe.What(), _( "Error writing page layout design file" ) );
  86. }
  87. }
  88. ~WS_DATA_MODEL_FILEIO()
  89. {
  90. delete m_fileout;
  91. }
  92. };
  93. // A helper class to write a page layout description to a string
  94. class WS_DATA_MODEL_STRINGIO: public WS_DATA_MODEL_IO
  95. {
  96. STRING_FORMATTER * m_writer;
  97. wxString & m_output;
  98. public:
  99. WS_DATA_MODEL_STRINGIO( wxString& aOutputString ):
  100. WS_DATA_MODEL_IO(), m_output( aOutputString )
  101. {
  102. try
  103. {
  104. m_writer = new STRING_FORMATTER();
  105. m_out = m_writer;
  106. }
  107. catch( const IO_ERROR& ioe )
  108. {
  109. wxMessageBox( ioe.What(), _( "Error writing page layout design file" ) );
  110. }
  111. }
  112. ~WS_DATA_MODEL_STRINGIO()
  113. {
  114. m_output = FROM_UTF8( m_writer->GetString().c_str() );
  115. delete m_writer;
  116. }
  117. };
  118. /*
  119. * Save the description in a file
  120. */
  121. void WS_DATA_MODEL::Save( const wxString& aFullFileName )
  122. {
  123. WS_DATA_MODEL_FILEIO writer( aFullFileName );
  124. writer.Format( this );
  125. }
  126. /* Save the description in a buffer
  127. */
  128. void WS_DATA_MODEL::SaveInString( wxString& aOutputString )
  129. {
  130. WS_DATA_MODEL_STRINGIO writer( aOutputString );
  131. writer.Format( this );
  132. }
  133. void WS_DATA_MODEL::SaveInString( std::vector<WS_DATA_ITEM*> aItemsList, wxString& aOutputString )
  134. {
  135. WS_DATA_MODEL_STRINGIO writer( aOutputString );
  136. LOCALE_IO toggle; // switch on/off the locale "C" notation
  137. for( WS_DATA_ITEM* item : aItemsList )
  138. writer.Format( this, item, 0 );
  139. }
  140. void WS_DATA_MODEL_IO::Format( WS_DATA_MODEL* aModel, WS_DATA_ITEM* aItem, int aNestLevel ) const
  141. {
  142. switch( aItem->GetType() )
  143. {
  144. case WS_DATA_ITEM::WS_TEXT:
  145. format( (WS_DATA_ITEM_TEXT*) aItem, aNestLevel );
  146. break;
  147. case WS_DATA_ITEM::WS_SEGMENT:
  148. case WS_DATA_ITEM::WS_RECT:
  149. format( aModel, aItem, aNestLevel );
  150. break;
  151. case WS_DATA_ITEM::WS_POLYPOLYGON:
  152. format( (WS_DATA_ITEM_POLYGONS*) aItem, aNestLevel );
  153. break;
  154. case WS_DATA_ITEM::WS_BITMAP:
  155. format( (WS_DATA_ITEM_BITMAP*) aItem, aNestLevel );
  156. break;
  157. default:
  158. wxFAIL_MSG( wxT( "Cannot format item" ) );
  159. }
  160. }
  161. void WS_DATA_MODEL_IO::Format( WS_DATA_MODEL* aPageLayout ) const
  162. {
  163. LOCALE_IO toggle; // switch on/off the locale "C" notation
  164. m_out->Print( 0, "(page_layout\n" );
  165. // Setup
  166. int nestLevel = 1;
  167. // Write default values:
  168. m_out->Print( nestLevel, "(%s ", getTokenName( T_setup ) );
  169. m_out->Print( 0, "(textsize %s %s)",
  170. double2Str( aPageLayout->m_DefaultTextSize.x ).c_str(),
  171. double2Str( aPageLayout->m_DefaultTextSize.y ).c_str() );
  172. m_out->Print( 0, "(linewidth %s)",
  173. double2Str( aPageLayout->m_DefaultLineWidth ).c_str() );
  174. m_out->Print( 0, "(textlinewidth %s)",
  175. double2Str( aPageLayout->m_DefaultTextThickness ).c_str() );
  176. m_out->Print( 0, "\n" );
  177. // Write margin values
  178. m_out->Print( nestLevel, "(%s %s)", getTokenName( T_left_margin ),
  179. double2Str( aPageLayout->GetLeftMargin() ).c_str() );
  180. m_out->Print( 0, "(%s %s)", getTokenName( T_right_margin ),
  181. double2Str( aPageLayout->GetRightMargin() ).c_str() );
  182. m_out->Print( 0, "(%s %s)", getTokenName( T_top_margin ),
  183. double2Str( aPageLayout->GetTopMargin() ).c_str() );
  184. m_out->Print( 0, "(%s %s)", getTokenName( T_bottom_margin ),
  185. double2Str( aPageLayout->GetBottomMargin() ).c_str() );
  186. m_out->Print( 0, ")\n" );
  187. // Save the graphical items on the page layout
  188. for( unsigned ii = 0; ii < aPageLayout->GetCount(); ii++ )
  189. {
  190. WS_DATA_ITEM* item = aPageLayout->GetItem( ii );
  191. Format( aPageLayout, item, nestLevel );
  192. }
  193. m_out->Print( 0, ")\n" );
  194. }
  195. void WS_DATA_MODEL_IO::format( WS_DATA_ITEM_TEXT* aItem, int aNestLevel ) const
  196. {
  197. m_out->Print( aNestLevel, "(%s", getTokenName( T_tbtext ) );
  198. m_out->Print( 0, " %s", m_out->Quotew( aItem->m_TextBase ).c_str() );
  199. m_out->Print( 0, " (%s %s)", getTokenName( T_name ),
  200. m_out->Quotew( aItem->m_Name ).c_str() );
  201. formatCoordinate( getTokenName( T_pos ), aItem->m_Pos );
  202. formatOptions( aItem );
  203. if( aItem->m_Orient )
  204. m_out->Print( 0, " (%s %s)", getTokenName( T_rotate ),
  205. double2Str(aItem->m_Orient ).c_str() );
  206. // Write font info, only if it is not the default setup
  207. bool write_size = aItem->m_TextSize.x != 0.0 || aItem->m_TextSize.y != 0.0;
  208. bool write_thickness = aItem->m_LineWidth != 0.0;
  209. if( write_thickness || write_size || aItem->m_Bold || aItem->m_Italic )
  210. {
  211. m_out->Print( 0, " (%s", getTokenName( T_font ) );
  212. if( write_thickness )
  213. {
  214. m_out->Print( 0, " (%s %s)", getTokenName( T_linewidth ),
  215. double2Str(aItem->m_LineWidth ).c_str() );
  216. }
  217. if( write_size )
  218. {
  219. m_out->Print( 0, " (%s %s %s)", getTokenName( T_size ),
  220. double2Str(aItem->m_TextSize.x ).c_str(),
  221. double2Str(aItem->m_TextSize.y ).c_str() );
  222. }
  223. if( aItem->m_Bold )
  224. m_out->Print( 0, " %s", getTokenName( T_bold ) );
  225. if( aItem->m_Italic )
  226. m_out->Print( 0, " %s", getTokenName( T_italic ) );
  227. m_out->Print( 0, ")" );
  228. }
  229. // Write text justification
  230. if( aItem->m_Hjustify != GR_TEXT_HJUSTIFY_LEFT ||
  231. aItem->m_Vjustify != GR_TEXT_VJUSTIFY_CENTER )
  232. {
  233. m_out->Print( 0, " (%s", getTokenName( T_justify ) );
  234. // Write T_center opt first, because it is
  235. // also a center for both m_Hjustify and m_Vjustify
  236. if( aItem->m_Hjustify == GR_TEXT_HJUSTIFY_CENTER )
  237. m_out->Print( 0, " %s", getTokenName( T_center ) );
  238. if( aItem->m_Hjustify == GR_TEXT_HJUSTIFY_RIGHT )
  239. m_out->Print( 0, " %s", getTokenName( T_right ) );
  240. if( aItem->m_Vjustify == GR_TEXT_VJUSTIFY_TOP )
  241. m_out->Print( 0, " %s", getTokenName( T_top ) );
  242. if( aItem->m_Vjustify == GR_TEXT_VJUSTIFY_BOTTOM )
  243. m_out->Print( 0, " %s", getTokenName( T_bottom ) );
  244. m_out->Print( 0, ")" );
  245. }
  246. // write constraints
  247. if( aItem->m_BoundingBoxSize.x )
  248. m_out->Print( 0, " (%s %s)", getTokenName( T_maxlen ),
  249. double2Str(aItem->m_BoundingBoxSize.x ).c_str() );
  250. if( aItem->m_BoundingBoxSize.y )
  251. m_out->Print( 0, " (%s %s)", getTokenName( T_maxheight ),
  252. double2Str(aItem->m_BoundingBoxSize.y ).c_str() );
  253. formatRepeatParameters( aItem );
  254. m_out->Print( 0, ")\n" );
  255. }
  256. void WS_DATA_MODEL_IO::format( WS_DATA_MODEL* aModel, WS_DATA_ITEM* aItem, int aNestLevel ) const
  257. {
  258. if( aItem->GetType() == WS_DATA_ITEM::WS_RECT )
  259. m_out->Print( aNestLevel, "(%s", getTokenName( T_rect ) );
  260. else
  261. m_out->Print( aNestLevel, "(%s", getTokenName( T_line ) );
  262. m_out->Print( 0, " (%s %s)", getTokenName( T_name ),
  263. m_out->Quotew( aItem->m_Name ).c_str() );
  264. formatCoordinate( getTokenName( T_start ), aItem->m_Pos );
  265. formatCoordinate( getTokenName( T_end ), aItem->m_End );
  266. formatOptions( aItem );
  267. if( aItem->m_LineWidth && aItem->m_LineWidth != aModel->m_DefaultLineWidth )
  268. m_out->Print( 0, " (linewidth %s)", double2Str( aItem->m_LineWidth ).c_str() );
  269. formatRepeatParameters( aItem );
  270. m_out->Print( 0, ")\n" );
  271. }
  272. void WS_DATA_MODEL_IO::format( WS_DATA_ITEM_POLYGONS* aItem, int aNestLevel ) const
  273. {
  274. m_out->Print( aNestLevel, "(%s", getTokenName( T_polygon ) );
  275. m_out->Print( 0, " (%s %s)", getTokenName( T_name ),
  276. m_out->Quotew( aItem->m_Name ).c_str() );
  277. formatCoordinate( getTokenName( T_pos ), aItem->m_Pos );
  278. formatOptions( aItem );
  279. formatRepeatParameters( aItem );
  280. if( aItem->m_Orient )
  281. m_out->Print( 0, " (%s %s)", getTokenName( T_rotate ),
  282. double2Str(aItem->m_Orient ).c_str() );
  283. if( aItem->m_LineWidth )
  284. m_out->Print( 0, " (linewidth %s)\n", double2Str( aItem->m_LineWidth ).c_str() );
  285. // Write polygon corners list
  286. for( int kk = 0; kk < aItem->GetPolyCount(); kk++ )
  287. {
  288. m_out->Print( aNestLevel+1, "(%s", getTokenName( T_pts ) );
  289. // Create current polygon corners list
  290. unsigned ist = aItem->GetPolyIndexStart( kk );
  291. unsigned iend = aItem->GetPolyIndexEnd( kk );
  292. int ii = 0;
  293. while( ist <= iend )
  294. {
  295. DPOINT pos = aItem->m_Corners[ist++];
  296. int nestLevel = 0;
  297. if( ii++ > 4)
  298. {
  299. m_out->Print( 0, "\n" );
  300. nestLevel = aNestLevel+2;
  301. ii = 0;
  302. }
  303. m_out->Print( nestLevel, " (%s %s %s)", getTokenName( T_xy ),
  304. double2Str( pos.x ).c_str(),
  305. double2Str( pos.y ).c_str() );
  306. }
  307. m_out->Print( 0, ")\n" );
  308. }
  309. m_out->Print( aNestLevel, ")\n" );
  310. }
  311. void WS_DATA_MODEL_IO::format( WS_DATA_ITEM_BITMAP* aItem, int aNestLevel ) const
  312. {
  313. m_out->Print( aNestLevel, "(%s", getTokenName( T_bitmap ) );
  314. m_out->Print( 0, " (%s %s)", getTokenName( T_name ),
  315. m_out->Quotew( aItem->m_Name ).c_str() );
  316. formatCoordinate( getTokenName( T_pos ), aItem->m_Pos );
  317. formatOptions( aItem );
  318. m_out->Print( 0, " (%s %s)", getTokenName( T_scale ),
  319. double2Str( aItem->m_ImageBitmap->GetScale() ).c_str() );
  320. formatRepeatParameters( aItem );
  321. m_out->Print( 0,"\n");
  322. // Write image in png readable format
  323. m_out->Print( aNestLevel, "(%s\n", getTokenName( T_pngdata ) );
  324. wxArrayString pngStrings;
  325. aItem->m_ImageBitmap->SaveData( pngStrings );
  326. for( unsigned ii = 0; ii < pngStrings.GetCount(); ii++ )
  327. m_out->Print( aNestLevel+1, "(data \"%s\")\n", TO_UTF8(pngStrings[ii]) );
  328. m_out->Print( aNestLevel+1, ")\n" );
  329. m_out->Print( aNestLevel, ")\n" );
  330. }
  331. void WS_DATA_MODEL_IO::formatCoordinate( const char * aToken,
  332. POINT_COORD & aCoord ) const
  333. {
  334. m_out->Print( 0, " (%s %s %s", aToken,
  335. double2Str( aCoord.m_Pos.x ).c_str(),
  336. double2Str( aCoord.m_Pos.y ).c_str() );
  337. switch( aCoord.m_Anchor )
  338. {
  339. case RB_CORNER: break;
  340. case LT_CORNER: m_out->Print( 0, " %s", getTokenName( T_ltcorner ) ); break;
  341. case LB_CORNER: m_out->Print( 0, " %s", getTokenName( T_lbcorner ) ); break;
  342. case RT_CORNER: m_out->Print( 0, " %s", getTokenName( T_rtcorner ) ); break;
  343. }
  344. m_out->Print( 0, ")" );
  345. }
  346. void WS_DATA_MODEL_IO::formatRepeatParameters( WS_DATA_ITEM* aItem ) const
  347. {
  348. if( aItem->m_RepeatCount <= 1 )
  349. return;
  350. m_out->Print( 0, " (repeat %d)", aItem->m_RepeatCount );
  351. if( aItem->m_IncrementVector.x )
  352. m_out->Print( 0, " (incrx %s)", double2Str(aItem-> m_IncrementVector.x ).c_str() );
  353. if( aItem->m_IncrementVector.y )
  354. m_out->Print( 0, " (incry %s)", double2Str( aItem->m_IncrementVector.y ).c_str() );
  355. if( aItem->m_IncrementLabel != 1 && aItem->GetType() == WS_DATA_ITEM::WS_TEXT )
  356. m_out->Print( 0, " (incrlabel %d)", aItem->m_IncrementLabel );
  357. }
  358. void WS_DATA_MODEL_IO::formatOptions( WS_DATA_ITEM* aItem ) const
  359. {
  360. if( aItem->GetPage1Option() == FIRST_PAGE_ONLY )
  361. m_out->Print( 0, " (%s %s)", getTokenName( T_option ), getTokenName(T_page1only ) );
  362. else if( aItem->GetPage1Option() == SUBSEQUENT_PAGES )
  363. m_out->Print( 0, " (%s %s)", getTokenName( T_option ), getTokenName( T_notonpage1 ) );
  364. }