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.

792 lines
20 KiB

  1. /**
  2. * @file common/page_layout/page_layout_reader.cpp
  3. * @brief read 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) 1992-2013 Jean-Pierre Charras <jp.charras at wanadoo.fr>.
  10. * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
  11. *
  12. *
  13. * This program is free software; you can redistribute it and/or
  14. * modify it under the terms of the GNU General Public License
  15. * as published by the Free Software Foundation; either version 2
  16. * of the License, or (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program; if not, you may find one here:
  25. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  26. * or you may search the http://www.gnu.org website for the version 2 license,
  27. * or you may write to the Free Software Foundation, Inc.,
  28. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  29. */
  30. #include <fctsys.h>
  31. #include <base_struct.h>
  32. #include <ws_painter.h>
  33. #include <ws_draw_item.h>
  34. #include <ws_data_model.h>
  35. #include <page_layout_reader_lexer.h>
  36. #include <wx/file.h>
  37. #include <wx/mstream.h>
  38. using namespace TB_READER_T;
  39. /**
  40. * Class PAGE_LAYOUT_READER_PARSER
  41. * holds data and functions pertinent to parsing a S-expression file
  42. * for a WS_DATA_MODEL.
  43. */
  44. class PAGE_LAYOUT_READER_PARSER : public PAGE_LAYOUT_READER_LEXER
  45. {
  46. public:
  47. PAGE_LAYOUT_READER_PARSER( const char* aLine, const wxString& aSource );
  48. void Parse( WS_DATA_MODEL* aLayout );
  49. private:
  50. /**
  51. * Function parseInt
  52. * parses an integer and constrains it between two values.
  53. * @param aMin is the smallest return value.
  54. * @param aMax is the largest return value.
  55. * @return int - the parsed integer.
  56. */
  57. int parseInt( int aMin, int aMax );
  58. /**
  59. * Function parseDouble
  60. * parses a double
  61. * @return double - the parsed double.
  62. */
  63. double parseDouble();
  64. void parseSetup( WS_DATA_MODEL* aLayout );
  65. /**
  66. * parse a graphic item starting by "(line" or "(rect" and read parameters.
  67. */
  68. void parseGraphic( WS_DATA_ITEM * aItem );
  69. /**
  70. * parse a text item starting by "(tbtext" and read parameters.
  71. */
  72. void parseText( WS_DATA_ITEM_TEXT * aItem );
  73. /**
  74. * parse a polygon item starting by "( polygon" and read parameters.
  75. * the list of corners included in this description is read by parsePolyOutline
  76. */
  77. void parsePolygon( WS_DATA_ITEM_POLYGONS * aItem );
  78. /**
  79. * parse a list of corners starting by "( pts" and read coordinates.
  80. */
  81. void parsePolyOutline( WS_DATA_ITEM_POLYGONS * aItem );
  82. /**
  83. * parse a bitmap item starting by "( bitmap" and read parameters.
  84. */
  85. void parseBitmap( WS_DATA_ITEM_BITMAP * aItem );
  86. void parseCoordinate( POINT_COORD& aCoord);
  87. void readOption( WS_DATA_ITEM * aItem );
  88. void readPngdata( WS_DATA_ITEM_BITMAP * aItem );
  89. };
  90. // PCB_PLOT_PARAMS_PARSER
  91. PAGE_LAYOUT_READER_PARSER::PAGE_LAYOUT_READER_PARSER( const char* aLine, const wxString& aSource ) :
  92. PAGE_LAYOUT_READER_LEXER( aLine, aSource )
  93. {
  94. }
  95. void PAGE_LAYOUT_READER_PARSER::Parse( WS_DATA_MODEL* aLayout )
  96. {
  97. WS_DATA_ITEM* item;
  98. LOCALE_IO toggle;
  99. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  100. {
  101. if( token == T_LEFT )
  102. token = NextTok();
  103. if( token == T_page_layout )
  104. continue;
  105. switch( token )
  106. {
  107. case T_setup: // Defines default values for graphic items
  108. parseSetup( aLayout );
  109. break;
  110. case T_line:
  111. item = new WS_DATA_ITEM( WS_DATA_ITEM::WS_SEGMENT );
  112. parseGraphic( item );
  113. aLayout->Append( item );
  114. break;
  115. case T_rect:
  116. item = new WS_DATA_ITEM( WS_DATA_ITEM::WS_RECT );
  117. parseGraphic( item );
  118. aLayout->Append( item );
  119. break;
  120. case T_polygon:
  121. item = new WS_DATA_ITEM_POLYGONS();
  122. parsePolygon( (WS_DATA_ITEM_POLYGONS*) item );
  123. aLayout->Append( item );
  124. break;
  125. case T_bitmap:
  126. item = new WS_DATA_ITEM_BITMAP( NULL );
  127. parseBitmap( (WS_DATA_ITEM_BITMAP*) item );
  128. aLayout->Append( item );
  129. break;
  130. case T_tbtext:
  131. NeedSYMBOLorNUMBER();
  132. item = new WS_DATA_ITEM_TEXT( FromUTF8() );
  133. parseText( (WS_DATA_ITEM_TEXT*) item );
  134. aLayout->Append( item );
  135. break;
  136. default:
  137. Unexpected( CurText() );
  138. break;
  139. }
  140. }
  141. }
  142. void PAGE_LAYOUT_READER_PARSER::parseSetup( WS_DATA_MODEL* aLayout )
  143. {
  144. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  145. {
  146. switch( token )
  147. {
  148. case T_LEFT:
  149. break;
  150. case T_linewidth:
  151. aLayout->m_DefaultLineWidth = parseDouble();
  152. NeedRIGHT();
  153. break;
  154. case T_textsize:
  155. aLayout->m_DefaultTextSize.x = parseDouble();
  156. aLayout->m_DefaultTextSize.y = parseDouble();
  157. NeedRIGHT();
  158. break;
  159. case T_textlinewidth:
  160. aLayout->m_DefaultTextThickness = parseDouble();
  161. NeedRIGHT();
  162. break;
  163. case T_left_margin:
  164. aLayout->SetLeftMargin( parseDouble() );
  165. NeedRIGHT();
  166. break;
  167. case T_right_margin:
  168. aLayout->SetRightMargin( parseDouble() );
  169. NeedRIGHT();
  170. break;
  171. case T_top_margin:
  172. aLayout->SetTopMargin( parseDouble() );
  173. NeedRIGHT();
  174. break;
  175. case T_bottom_margin:
  176. aLayout->SetBottomMargin( parseDouble() );
  177. NeedRIGHT();
  178. break;
  179. default:
  180. Unexpected( CurText() );
  181. break;
  182. }
  183. }
  184. }
  185. void PAGE_LAYOUT_READER_PARSER::parsePolygon( WS_DATA_ITEM_POLYGONS * aItem )
  186. {
  187. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  188. {
  189. if( token == T_LEFT )
  190. token = NextTok();
  191. switch( token )
  192. {
  193. case T_comment:
  194. NeedSYMBOLorNUMBER();
  195. aItem->m_Info = FromUTF8();
  196. NeedRIGHT();
  197. break;
  198. case T_pos:
  199. parseCoordinate( aItem->m_Pos );
  200. break;
  201. case T_name:
  202. NeedSYMBOLorNUMBER();
  203. aItem->m_Name = FromUTF8();
  204. NeedRIGHT();
  205. break;
  206. case T_option:
  207. readOption( aItem );
  208. break;
  209. case T_pts:
  210. parsePolyOutline( aItem );
  211. aItem->CloseContour();
  212. break;
  213. case T_rotate:
  214. aItem->m_Orient = parseDouble();
  215. NeedRIGHT();
  216. break;
  217. case T_repeat:
  218. aItem->m_RepeatCount = parseInt( -1, 100 );
  219. NeedRIGHT();
  220. break;
  221. case T_incrx:
  222. aItem->m_IncrementVector.x = parseDouble();
  223. NeedRIGHT();
  224. break;
  225. case T_incry:
  226. aItem->m_IncrementVector.y = parseDouble();
  227. NeedRIGHT();
  228. break;
  229. case T_linewidth:
  230. aItem->m_LineWidth = parseDouble();
  231. NeedRIGHT();
  232. break;
  233. default:
  234. Unexpected( CurText() );
  235. break;
  236. }
  237. }
  238. aItem->SetBoundingBox();
  239. }
  240. void PAGE_LAYOUT_READER_PARSER::parsePolyOutline( WS_DATA_ITEM_POLYGONS * aItem )
  241. {
  242. DPOINT corner;
  243. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  244. {
  245. if( token == T_LEFT )
  246. token = NextTok();
  247. switch( token )
  248. {
  249. case T_xy:
  250. corner.x = parseDouble();
  251. corner.y = parseDouble();
  252. aItem->AppendCorner( corner );
  253. NeedRIGHT();
  254. break;
  255. default:
  256. Unexpected( CurText() );
  257. break;
  258. }
  259. }
  260. }
  261. void PAGE_LAYOUT_READER_PARSER::parseBitmap( WS_DATA_ITEM_BITMAP * aItem )
  262. {
  263. BITMAP_BASE* image = new BITMAP_BASE;
  264. aItem->m_ImageBitmap = image;
  265. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  266. {
  267. if( token == T_LEFT )
  268. token = NextTok();
  269. switch( token )
  270. {
  271. case T_name:
  272. NeedSYMBOLorNUMBER();
  273. aItem->m_Name = FromUTF8();
  274. NeedRIGHT();
  275. break;
  276. case T_pos:
  277. parseCoordinate( aItem->m_Pos );
  278. break;
  279. case T_repeat:
  280. aItem->m_RepeatCount = parseInt( -1, 100 );
  281. NeedRIGHT();
  282. break;
  283. case T_incrx:
  284. aItem->m_IncrementVector.x = parseDouble();
  285. NeedRIGHT();
  286. break;
  287. case T_incry:
  288. aItem->m_IncrementVector.y = parseDouble();
  289. NeedRIGHT();
  290. break;
  291. case T_linewidth:
  292. aItem->m_LineWidth = parseDouble();
  293. NeedRIGHT();
  294. break;
  295. case T_scale:
  296. aItem->m_ImageBitmap->SetScale( parseDouble() );
  297. NeedRIGHT();
  298. break;
  299. case T_pngdata:
  300. readPngdata( aItem );
  301. break;
  302. case T_option:
  303. readOption( aItem );
  304. break;
  305. default:
  306. Unexpected( CurText() );
  307. break;
  308. }
  309. }
  310. }
  311. void PAGE_LAYOUT_READER_PARSER::readPngdata( WS_DATA_ITEM_BITMAP * aItem )
  312. {
  313. std::string tmp;
  314. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  315. {
  316. if( token == T_LEFT )
  317. token = NextTok();
  318. switch( token )
  319. {
  320. case T_data:
  321. NeedSYMBOLorNUMBER();
  322. tmp += CurStr();
  323. tmp += "\n";
  324. NeedRIGHT();
  325. break;
  326. default:
  327. Unexpected( CurText() );
  328. break;
  329. }
  330. }
  331. tmp += "EndData";
  332. wxString msg;
  333. STRING_LINE_READER str_reader( tmp, wxT("Png kicad_wks data") );
  334. if( ! aItem->m_ImageBitmap->LoadData( str_reader, msg ) )
  335. wxLogMessage(msg);
  336. }
  337. void PAGE_LAYOUT_READER_PARSER::readOption( WS_DATA_ITEM * aItem )
  338. {
  339. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  340. {
  341. switch( token )
  342. {
  343. case T_page1only: aItem->SetPage1Option( FIRST_PAGE_ONLY ); break;
  344. case T_notonpage1: aItem->SetPage1Option( SUBSEQUENT_PAGES ); break;
  345. default: Unexpected( CurText() ); break;
  346. }
  347. }
  348. }
  349. void PAGE_LAYOUT_READER_PARSER::parseGraphic( WS_DATA_ITEM * aItem )
  350. {
  351. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  352. {
  353. if( token == T_LEFT )
  354. token = NextTok();
  355. else
  356. {
  357. // If another token than T_LEFT is read here, this is an error
  358. // however, due to a old bug in kicad, the token T_end can be found
  359. // without T_LEFT in a very few .wks files (perhaps only one in a demo).
  360. // So this ugly hack disables the error detection.
  361. if( token != T_end )
  362. Unexpected( CurText() );
  363. }
  364. switch( token )
  365. {
  366. case T_comment:
  367. NeedSYMBOLorNUMBER();
  368. aItem->m_Info = FromUTF8();
  369. NeedRIGHT();
  370. break;
  371. case T_option:
  372. readOption( aItem );
  373. break;
  374. case T_name:
  375. NeedSYMBOLorNUMBER();
  376. aItem->m_Name = FromUTF8();
  377. NeedRIGHT();
  378. break;
  379. case T_start:
  380. parseCoordinate( aItem->m_Pos );
  381. break;
  382. case T_end:
  383. parseCoordinate( aItem->m_End );
  384. break;
  385. case T_repeat:
  386. aItem->m_RepeatCount = parseInt( -1, 100 );
  387. NeedRIGHT();
  388. break;
  389. case T_incrx:
  390. aItem->m_IncrementVector.x = parseDouble();
  391. NeedRIGHT();
  392. break;
  393. case T_incry:
  394. aItem->m_IncrementVector.y = parseDouble();
  395. NeedRIGHT();
  396. break;
  397. case T_linewidth:
  398. aItem->m_LineWidth = parseDouble();
  399. NeedRIGHT();
  400. break;
  401. default:
  402. Unexpected( CurText() );
  403. break;
  404. }
  405. }
  406. }
  407. void PAGE_LAYOUT_READER_PARSER::parseText( WS_DATA_ITEM_TEXT* aItem )
  408. {
  409. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  410. {
  411. if( token == T_LEFT )
  412. token = NextTok();
  413. switch( token )
  414. {
  415. case T_comment:
  416. NeedSYMBOLorNUMBER();
  417. aItem->m_Info = FromUTF8();
  418. NeedRIGHT();
  419. break;
  420. case T_option:
  421. readOption( aItem );
  422. break;
  423. case T_name:
  424. NeedSYMBOLorNUMBER();
  425. aItem->m_Name = FromUTF8();
  426. NeedRIGHT();
  427. break;
  428. case T_pos:
  429. parseCoordinate( aItem->m_Pos );
  430. break;
  431. case T_repeat:
  432. aItem->m_RepeatCount = parseInt( -1, 100 );
  433. NeedRIGHT();
  434. break;
  435. case T_incrx:
  436. aItem->m_IncrementVector.x = parseDouble();
  437. NeedRIGHT();
  438. break;
  439. case T_incry:
  440. aItem->m_IncrementVector.y = parseDouble();
  441. NeedRIGHT();
  442. break;
  443. case T_incrlabel:
  444. aItem->m_IncrementLabel = parseInt(INT_MIN, INT_MAX);
  445. NeedRIGHT();
  446. break;
  447. case T_maxlen:
  448. aItem->m_BoundingBoxSize.x = parseDouble();
  449. NeedRIGHT();
  450. break;
  451. case T_maxheight:
  452. aItem->m_BoundingBoxSize.y = parseDouble();
  453. NeedRIGHT();
  454. break;
  455. case T_font:
  456. for( token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  457. {
  458. switch( token )
  459. {
  460. case T_LEFT:
  461. break;
  462. case T_bold:
  463. aItem->m_Bold = true;
  464. break;
  465. case T_italic:
  466. aItem->m_Italic = true;
  467. break;
  468. case T_size:
  469. aItem->m_TextSize.x = parseDouble();
  470. aItem->m_TextSize.y = parseDouble();
  471. NeedRIGHT();
  472. break;
  473. case T_linewidth:
  474. aItem->m_LineWidth = parseDouble();
  475. NeedRIGHT();
  476. break;
  477. default:
  478. Unexpected( CurText() );
  479. break;
  480. }
  481. }
  482. break;
  483. case T_justify:
  484. for( token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  485. {
  486. switch( token )
  487. {
  488. case T_center:
  489. aItem->m_Hjustify = GR_TEXT_HJUSTIFY_CENTER;
  490. aItem->m_Vjustify = GR_TEXT_VJUSTIFY_CENTER;
  491. break;
  492. case T_left:
  493. aItem->m_Hjustify = GR_TEXT_HJUSTIFY_LEFT;
  494. break;
  495. case T_right:
  496. aItem->m_Hjustify = GR_TEXT_HJUSTIFY_RIGHT;
  497. break;
  498. case T_top:
  499. aItem->m_Vjustify = GR_TEXT_VJUSTIFY_TOP;
  500. break;
  501. case T_bottom:
  502. aItem->m_Vjustify = GR_TEXT_VJUSTIFY_BOTTOM;
  503. break;
  504. default:
  505. Unexpected( CurText() );
  506. break;
  507. }
  508. }
  509. break;
  510. case T_rotate:
  511. aItem->m_Orient = parseDouble();
  512. NeedRIGHT();
  513. break;
  514. default:
  515. Unexpected( CurText() );
  516. break;
  517. }
  518. }
  519. }
  520. // parse an expression like " 25 1 ltcorner)"
  521. void PAGE_LAYOUT_READER_PARSER::parseCoordinate( POINT_COORD& aCoord)
  522. {
  523. aCoord.m_Pos.x = parseDouble();
  524. aCoord.m_Pos.y = parseDouble();
  525. for( T token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
  526. {
  527. switch( token )
  528. {
  529. case T_ltcorner: aCoord.m_Anchor = LT_CORNER; break;
  530. case T_lbcorner: aCoord.m_Anchor = LB_CORNER; break;
  531. case T_rbcorner: aCoord.m_Anchor = RB_CORNER; break;
  532. case T_rtcorner: aCoord.m_Anchor = RT_CORNER; break;
  533. default: Unexpected( CurText() ); break;
  534. }
  535. }
  536. }
  537. int PAGE_LAYOUT_READER_PARSER::parseInt( int aMin, int aMax )
  538. {
  539. T token = NextTok();
  540. if( token != T_NUMBER )
  541. Expecting( T_NUMBER );
  542. int val = atoi( CurText() );
  543. if( val < aMin )
  544. val = aMin;
  545. else if( val > aMax )
  546. val = aMax;
  547. return val;
  548. }
  549. double PAGE_LAYOUT_READER_PARSER::parseDouble()
  550. {
  551. T token = NextTok();
  552. if( token != T_NUMBER )
  553. Expecting( T_NUMBER );
  554. double val = strtod( CurText(), NULL );
  555. return val;
  556. }
  557. // defaultPageLayout is the default page layout description
  558. // using the S expr.
  559. // see page_layout_default_shape.cpp
  560. extern const char defaultPageLayout[];
  561. void WS_DATA_MODEL::SetDefaultLayout()
  562. {
  563. SetPageLayout( defaultPageLayout, false, wxT( "default page" ) );
  564. }
  565. // Returns defaultPageLayout as a string;
  566. wxString WS_DATA_MODEL::DefaultLayout()
  567. {
  568. return wxString( defaultPageLayout );
  569. }
  570. // emptyPageLayout is a "empty" page layout description
  571. // there is a 0 length line to fool something somewhere.
  572. // using the S expr.
  573. // see page_layout_empty_description.cpp
  574. extern const char emptyPageLayout[];
  575. void WS_DATA_MODEL::SetEmptyLayout()
  576. {
  577. SetPageLayout( emptyPageLayout, false, wxT( "empty page" ) );
  578. }
  579. wxString WS_DATA_MODEL::EmptyLayout()
  580. {
  581. return wxString( emptyPageLayout );
  582. }
  583. void WS_DATA_MODEL::SetPageLayout( const char* aPageLayout, bool Append, const wxString& aSource )
  584. {
  585. if( ! Append )
  586. ClearList();
  587. PAGE_LAYOUT_READER_PARSER lp_parser( aPageLayout, wxT( "Sexpr_string" ) );
  588. try
  589. {
  590. lp_parser.Parse( this );
  591. }
  592. catch( const IO_ERROR& ioe )
  593. {
  594. wxLogMessage( ioe.What() );
  595. }
  596. }
  597. void WS_DATA_MODEL::SetPageLayout( const wxString& aFullFileName, bool Append )
  598. {
  599. wxString fullFileName = aFullFileName;
  600. if( !Append )
  601. {
  602. if( fullFileName.IsEmpty() )
  603. wxGetEnv( wxT( "KICAD_WKSFILE" ), &fullFileName );
  604. if( fullFileName.IsEmpty() || !wxFileExists( fullFileName ) )
  605. {
  606. #if 0
  607. if( !fullFileName.IsEmpty() )
  608. wxLogMessage( wxT( "Page layout file <%s> not found" ), fullFileName.GetData() );
  609. #endif
  610. SetDefaultLayout();
  611. return;
  612. }
  613. }
  614. wxFile wksFile( fullFileName );
  615. if( ! wksFile.IsOpened() )
  616. {
  617. if( !Append )
  618. SetDefaultLayout();
  619. return;
  620. }
  621. int filelen = wksFile.Length();
  622. char * buffer = new char[filelen+10];
  623. if( wksFile.Read( buffer, filelen ) != filelen )
  624. wxLogMessage( _("The file \"%s\" was not fully read"), fullFileName.GetData() );
  625. else
  626. {
  627. buffer[filelen]=0;
  628. if( ! Append )
  629. ClearList();
  630. PAGE_LAYOUT_READER_PARSER pl_parser( buffer, fullFileName );
  631. try
  632. {
  633. pl_parser.Parse( this );
  634. }
  635. catch( const IO_ERROR& ioe )
  636. {
  637. wxLogMessage( ioe.What() );
  638. }
  639. }
  640. delete[] buffer;
  641. }