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.

799 lines
28 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file gbr_metadata.cpp
  26. * @brief helper functions to handle the gerber metadata in files,
  27. * related to the netlist info and aperture attribute.
  28. */
  29. #include <wx/string.h>
  30. #include <wx/datetime.h>
  31. #include <gbr_metadata.h>
  32. #include <utf8.h>
  33. wxString GbrMakeCreationDateAttributeString( GBR_NC_STRING_FORMAT aFormat )
  34. {
  35. // creates the CreationDate attribute:
  36. // The attribute value must conform to the full version of the ISO 8601
  37. // date and time format, including time and time zone. Note that this is
  38. // the date the Gerber file was effectively created,
  39. // not the time the project of PCB was started
  40. wxDateTime date( wxDateTime::GetTimeNow() );
  41. // Date format: see http://www.cplusplus.com/reference/ctime/strftime
  42. wxString timezone_offset; // ISO 8601 offset from UTC in timezone
  43. timezone_offset = date.Format( "%z" ); // Extract the time zone offset
  44. // The time zone offset format is +mm or +hhmm (or -mm or -hhmm)
  45. // (mm = number of minutes, hh = number of hours. 1h00mn is returned as +0100)
  46. // we want +(or -) hh:mm
  47. if( timezone_offset.Len() > 3 ) // format +hhmm or -hhmm found
  48. // Add separator between hours and minutes
  49. timezone_offset.insert( 3, ":", 1 );
  50. wxString msg;
  51. switch( aFormat )
  52. {
  53. case GBR_NC_STRING_FORMAT_X2:
  54. msg.Printf( "%%TF.CreationDate,%s%s*%%", date.FormatISOCombined(), timezone_offset );
  55. break;
  56. case GBR_NC_STRING_FORMAT_X1:
  57. msg.Printf( "G04 #@! TF.CreationDate,%s%s*", date.FormatISOCombined(), timezone_offset );
  58. break;
  59. case GBR_NC_STRING_FORMAT_GBRJOB:
  60. msg.Printf( "%s%s", date.FormatISOCombined(), timezone_offset );
  61. break;
  62. case GBR_NC_STRING_FORMAT_NCDRILL:
  63. msg.Printf( "; #@! TF.CreationDate,%s%s", date.FormatISOCombined(), timezone_offset );
  64. break;
  65. }
  66. return msg;
  67. }
  68. wxString GbrMakeProjectGUIDfromString( const wxString& aText )
  69. {
  70. /* Gerber GUID format should be RFC4122 Version 1 or 4.
  71. * See en.wikipedia.org/wiki/Universally_unique_identifier
  72. * The format is:
  73. * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
  74. * with
  75. * x = hexDigit lower/upper case
  76. * and
  77. * M = '1' or '4' (UUID version: 1 (basic) or 4 (random)) (we use 4: UUID random)
  78. * and
  79. * N = '8' or '9' or 'A|a' or 'B|b' : UUID variant 1: 2 MSB bits have meaning) (we use N = 9)
  80. * N = 1000 or 1001 or 1010 or 1011 : 10xx means Variant 1 (Variant2: 110x and 111x are
  81. * reserved)
  82. */
  83. wxString guid;
  84. // Build a 32 digits GUID from the board name:
  85. // guid has 32 digits, so add chars in name to be sure we can build a 32 digits guid
  86. // (i.e. from a 16 char string name)
  87. // In fact only 30 digits are used, and 2 UID id
  88. wxString bname = aText;
  89. int cnt = 16 - bname.Len();
  90. if( cnt > 0 )
  91. bname.Append( 'X', cnt );
  92. int chr_idx = 0;
  93. // Output the 8 first hex digits:
  94. for( unsigned ii = 0; ii < 4; ii++ )
  95. {
  96. int cc = int( bname[chr_idx++] ) & 0xFF;
  97. guid << wxString::Format( "%2.2x", cc );
  98. }
  99. // Output the 4 next hex digits:
  100. guid << '-';
  101. for( unsigned ii = 0; ii < 2; ii++ )
  102. {
  103. int cc = int( bname[chr_idx++] ) & 0xFF;
  104. guid << wxString::Format( "%2.2x", cc );
  105. }
  106. // Output the 4 next hex digits (UUID version and 3 digits):
  107. guid << "-4"; // first digit: UUID version 4 (M = 4)
  108. {
  109. int cc = int( bname[chr_idx++] ) << 4 & 0xFF0;
  110. cc += int( bname[chr_idx] ) >> 4 & 0x0F;
  111. guid << wxString::Format( "%3.3x", cc );
  112. }
  113. // Output the 4 next hex digits (UUID variant and 3 digits):
  114. guid << "-9"; // first digit: UUID variant 1 (N = 9)
  115. {
  116. int cc = (int( bname[chr_idx++] ) & 0x0F) << 8;
  117. cc += int( bname[chr_idx++] ) & 0xFF;
  118. guid << wxString::Format( "%3.3x", cc );
  119. }
  120. // Output the 12 last hex digits:
  121. guid << '-';
  122. for( unsigned ii = 0; ii < 6; ii++ )
  123. {
  124. int cc = int( bname[chr_idx++] ) & 0xFF;
  125. guid << wxString::Format( "%2.2x", cc );
  126. }
  127. return guid;
  128. }
  129. std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribute,
  130. bool aUseX1StructuredComment )
  131. {
  132. std::string attribute_string; // the specific aperture attribute (TA.xxx)
  133. std::string comment_string; // a optional G04 comment line to write before the TA. line
  134. // generate a string to print a Gerber Aperture attribute
  135. switch( aAttribute )
  136. {
  137. // Dummy value (aAttribute must be < GBR_APERTURE_ATTRIB_END).
  138. case GBR_APERTURE_ATTRIB_END:
  139. case GBR_APERTURE_ATTRIB_NONE: // idle command: do nothing
  140. break;
  141. case GBR_APERTURE_ATTRIB_ETCHEDCMP: // print info associated to an item
  142. // which connects 2 different nets
  143. // (Net tees, microwave component)
  144. attribute_string = "TA.AperFunction,EtchedComponent";
  145. break;
  146. case GBR_APERTURE_ATTRIB_CONDUCTOR: // print info associated to a track
  147. attribute_string = "TA.AperFunction,Conductor";
  148. break;
  149. case GBR_APERTURE_ATTRIB_EDGECUT: // print info associated to a board outline
  150. attribute_string = "TA.AperFunction,Profile";
  151. break;
  152. case GBR_APERTURE_ATTRIB_VIAPAD: // print info associated to a flashed via
  153. attribute_string = "TA.AperFunction,ViaPad";
  154. break;
  155. case GBR_APERTURE_ATTRIB_NONCONDUCTOR: // print info associated to a item on a copper layer
  156. // which is not a track (for instance a text)
  157. attribute_string = "TA.AperFunction,NonConductor";
  158. break;
  159. case GBR_APERTURE_ATTRIB_COMPONENTPAD: // print info associated to a flashed
  160. // through hole component on outer layer
  161. attribute_string = "TA.AperFunction,ComponentPad";
  162. break;
  163. case GBR_APERTURE_ATTRIB_SMDPAD_SMDEF: // print info associated to a flashed for SMD pad.
  164. // with solder mask defined from the copper shape
  165. // Excluded BGA pads which have their own type
  166. attribute_string = "TA.AperFunction,SMDPad,SMDef";
  167. break;
  168. case GBR_APERTURE_ATTRIB_SMDPAD_CUDEF: // print info associated to a flashed SMD pad with
  169. // a solder mask defined by the solder mask
  170. attribute_string = "TA.AperFunction,SMDPad,CuDef";
  171. break;
  172. case GBR_APERTURE_ATTRIB_BGAPAD_SMDEF: // print info associated to flashed BGA pads with
  173. // a solder mask defined by the copper shape
  174. attribute_string = "TA.AperFunction,BGAPad,SMDef";
  175. break;
  176. case GBR_APERTURE_ATTRIB_BGAPAD_CUDEF: // print info associated to a flashed BGA pad with
  177. // a solder mask defined by the solder mask
  178. attribute_string = "TA.AperFunction,BGAPad,CuDef";
  179. break;
  180. case GBR_APERTURE_ATTRIB_CONNECTORPAD:
  181. // print info associated to a flashed edge connector pad (outer layers)
  182. attribute_string = "TA.AperFunction,ConnectorPad";
  183. break;
  184. case GBR_APERTURE_ATTRIB_WASHERPAD:
  185. // print info associated to flashed mechanical pads (NPTH)
  186. attribute_string = "TA.AperFunction,WasherPad";
  187. break;
  188. case GBR_APERTURE_ATTRIB_HEATSINKPAD: // print info associated to a flashed heat sink pad
  189. // (typically for SMDs)
  190. attribute_string = "TA.AperFunction,HeatsinkPad";
  191. break;
  192. case GBR_APERTURE_ATTRIB_TESTPOINT: // print info associated to a flashed test point pad
  193. // (typically for SMDs)
  194. attribute_string = "TA.AperFunction,TestPad";
  195. break;
  196. case GBR_APERTURE_ATTRIB_FIDUCIAL_GLBL: // print info associated to a flashed fiducial pad
  197. // (typically for SMDs)
  198. attribute_string = "TA.AperFunction,FiducialPad,Global";
  199. break;
  200. case GBR_APERTURE_ATTRIB_FIDUCIAL_LOCAL: // print info associated to a flashed fiducial pad
  201. // (typically for SMDs)
  202. attribute_string = "TA.AperFunction,FiducialPad,Local";
  203. break;
  204. case GBR_APERTURE_ATTRIB_CASTELLATEDPAD:
  205. // print info associated to a flashed castellated pad (typically for SMDs)
  206. attribute_string = "TA.AperFunction,CastellatedPad";
  207. break;
  208. case GBR_APERTURE_ATTRIB_CASTELLATEDDRILL:
  209. // print info associated to a flashed castellated pad in drill files
  210. attribute_string = "TA.AperFunction,CastellatedDrill";
  211. break;
  212. case GBR_APERTURE_ATTRIB_VIADRILL: // print info associated to a via hole in drill files
  213. attribute_string = "TA.AperFunction,ViaDrill";
  214. break;
  215. case GBR_APERTURE_ATTRIB_CMP_DRILL: // print info associated to a component
  216. // round pad hole in drill files
  217. attribute_string = "TA.AperFunction,ComponentDrill";
  218. break;
  219. // print info associated to a component oblong pad hole in drill files
  220. // Same as a round pad hole, but is a specific aperture in drill file and
  221. // a G04 comment is added to the aperture function
  222. case GBR_APERTURE_ATTRIB_CMP_OBLONG_DRILL:
  223. comment_string = "aperture for slot hole";
  224. attribute_string = "TA.AperFunction,ComponentDrill";
  225. break;
  226. case GBR_APERTURE_ATTRIB_CMP_POSITION: // print info associated to a component
  227. // flashed shape at the component position
  228. // in placement files
  229. attribute_string = "TA.AperFunction,ComponentMain";
  230. break;
  231. case GBR_APERTURE_ATTRIB_PAD1_POSITION: // print info associated to a component
  232. // flashed shape at pad 1 position
  233. // (pad 1 is also pad A1 or pad AA1)
  234. // in placement files
  235. attribute_string = "TA.AperFunction,ComponentPin";
  236. break;
  237. case GBR_APERTURE_ATTRIB_PADOTHER_POSITION: // print info associated to a component
  238. // flashed shape at pads position (all but pad 1)
  239. // in placement files
  240. // Currently: (could be changed later) same as
  241. // GBR_APERTURE_ATTRIB_PADOTHER_POSITION
  242. attribute_string = "TA.AperFunction,ComponentPin";
  243. break;
  244. case GBR_APERTURE_ATTRIB_CMP_BODY: // print info associated to a component
  245. // print the component physical body
  246. // polygon in placement files
  247. attribute_string = "TA.AperFunction,ComponentOutline,Body";
  248. break;
  249. case GBR_APERTURE_ATTRIB_CMP_LEAD2LEAD: // print info associated to a component
  250. // print the component physical lead to lead
  251. // polygon in placement files
  252. attribute_string = "TA.AperFunction,ComponentOutline,Lead2Lead";
  253. break;
  254. case GBR_APERTURE_ATTRIB_CMP_FOOTPRINT: // print info associated to a component
  255. // print the component footprint bounding box
  256. // polygon in placement files
  257. attribute_string = "TA.AperFunction,ComponentOutline,Footprint";
  258. break;
  259. case GBR_APERTURE_ATTRIB_CMP_COURTYARD: // print info associated to a component
  260. // print the component courtyard
  261. // polygon in placement files
  262. attribute_string = "TA.AperFunction,ComponentOutline,Courtyard";
  263. break;
  264. break;
  265. }
  266. std::string full_attribute_string;
  267. wxString eol_string;
  268. if( !attribute_string.empty() )
  269. {
  270. if( !comment_string.empty() )
  271. {
  272. full_attribute_string = "G04 " + comment_string + "*\n";
  273. }
  274. if( aUseX1StructuredComment )
  275. {
  276. full_attribute_string += "G04 #@! ";
  277. eol_string = "*\n";
  278. }
  279. else
  280. {
  281. full_attribute_string += "%";
  282. eol_string = "*%\n";
  283. }
  284. }
  285. full_attribute_string += attribute_string + eol_string;
  286. return full_attribute_string;
  287. }
  288. // Helper function to convert a ascii hex char to its integer value
  289. // If the char is not a hexa char, return -1
  290. int char2Hex( unsigned aCode )
  291. {
  292. if( aCode >= '0' && aCode <= '9' )
  293. return aCode - '0';
  294. if( aCode >= 'A' && aCode <= 'F' )
  295. return aCode - 'A' + 10;
  296. if( aCode >= 'a' && aCode <= 'f' )
  297. return aCode - 'a' + 10;
  298. return -1;
  299. }
  300. wxString FormatStringFromGerber( const wxString& aString )
  301. {
  302. // make the inverse conversion of FormatStringToGerber()
  303. // It converts a "normalized" gerber string containing escape sequences
  304. // and convert it to a 16 bits Unicode char
  305. // and return a wxString (Unicode 16) from the gerber string
  306. // Note the initial gerber string can already contain Unicode chars.
  307. wxString txt; // The string converted from Gerber string
  308. unsigned count = aString.Length();
  309. for( unsigned ii = 0; ii < count; ++ii )
  310. {
  311. unsigned code = aString[ii];
  312. if( code == '\\' && ii < count-5 && aString[ii+1] == 'u' )
  313. {
  314. // Note the latest Gerber X2 spec (2019 06) uses \uXXXX to encode
  315. // the Unicode XXXX hexadecimal value
  316. // If 4 chars next to 'u' are hexadecimal chars,
  317. // Convert these 4 hexadecimal digits to a 16 bit Unicode
  318. // (Gerber allows only 4 hexadecimal digits)
  319. // If an error occurs, the escape sequence is not translated,
  320. // and used "as this"
  321. long value = 0;
  322. bool error = false;
  323. for( int jj = 0; jj < 4; jj++ )
  324. {
  325. value <<= 4;
  326. code = aString[ii+jj+2];
  327. int hexa = char2Hex( code );
  328. if( hexa >= 0 )
  329. value += hexa;
  330. else
  331. {
  332. error = true;
  333. break;
  334. }
  335. }
  336. if( !error )
  337. {
  338. if( value >= ' ' ) // Is a valid wxChar ?
  339. txt.Append( wxChar( value ) );
  340. ii += 5;
  341. }
  342. else
  343. {
  344. txt.Append( aString[ii] );
  345. continue;
  346. }
  347. }
  348. else
  349. {
  350. txt.Append( aString[ii] );
  351. }
  352. }
  353. return txt;
  354. }
  355. wxString ConvertNotAllowedCharsInGerber( const wxString& aString, bool aAllowUtf8Chars,
  356. bool aQuoteString )
  357. {
  358. /* format string means convert any code > 0x7E and unauthorized codes to a hexadecimal
  359. * 16 bits sequence Unicode
  360. * However if aAllowUtf8Chars is true only unauthorized codes will be escaped, because some
  361. * Gerber files accept UTF8 chars.
  362. * unauthorized codes are ',' '*' '%' '\' '"' and are used as separators in Gerber files
  363. */
  364. wxString txt;
  365. if( aQuoteString )
  366. txt << "\"";
  367. for( unsigned ii = 0; ii < aString.Length(); ++ii )
  368. {
  369. wxChar code = aString[ii];
  370. bool convert = false;
  371. switch( code )
  372. {
  373. case '\\':
  374. case '%':
  375. case '*':
  376. case ',':
  377. convert = true;
  378. break;
  379. case '"':
  380. if( aQuoteString )
  381. convert = true;
  382. break;
  383. default:
  384. break;
  385. }
  386. if( !aAllowUtf8Chars && code > 0x7F )
  387. convert = true;
  388. if( convert )
  389. {
  390. // Convert code to 4 hexadecimal digit
  391. // (Gerber allows only 4 hexadecimal digit) in escape seq:
  392. // "\uXXXX", XXXX is the Unicode 16 bits hexa value
  393. char hexa[32];
  394. sprintf( hexa,"\\u%4.4X", code & 0xFFFF);
  395. txt += hexa;
  396. }
  397. else
  398. {
  399. txt += code;
  400. }
  401. }
  402. if( aQuoteString )
  403. txt << "\"";
  404. return txt;
  405. }
  406. std::string GBR_DATA_FIELD::GetGerberString() const
  407. {
  408. wxString converted;
  409. if( !m_field.IsEmpty() )
  410. converted = ConvertNotAllowedCharsInGerber( m_field, m_useUTF8, m_escapeString );
  411. // Convert the char string to std::string. Be careful when converting a wxString to
  412. // a std::string: using static_cast<const char*> is mandatory
  413. std::string txt = static_cast<const char*>( converted.utf8_str() );
  414. return txt;
  415. }
  416. std::string FormatStringToGerber( const wxString& aString )
  417. {
  418. wxString converted;
  419. /* format string means convert any code > 0x7E and unauthorized codes to a hexadecimal
  420. * 16 bits sequence Unicode
  421. * unauthorized codes are ',' '*' '%' '\'
  422. * This conversion is not made for quoted strings, because if the string is
  423. * quoted, the conversion is expected to be already made, and the returned string must use
  424. * UTF8 encoding
  425. */
  426. if( !aString.IsEmpty() && ( aString[0] != '\"' || aString[aString.Len()-1] != '\"' ) )
  427. converted = ConvertNotAllowedCharsInGerber( aString, false, false );
  428. else
  429. converted = aString;
  430. // Convert the char string to std::string. Be careful when converting a wxString to
  431. // a std::string: using static_cast<const char*> is mandatory
  432. std::string txt = static_cast<const char*>( converted.utf8_str() );
  433. return txt;
  434. }
  435. // Netname and Pan num fields cannot be empty in Gerber files
  436. // Normalized names must be used, if any
  437. #define NO_NET_NAME wxT( "N/C" ) // net name of not connected pads (one pad net) (normalized)
  438. #define NO_PAD_NAME wxT( "" ) // pad name of pads without pad name/number (not normalized)
  439. bool FormatNetAttribute( std::string& aPrintedText, std::string& aLastNetAttributes,
  440. const GBR_NETLIST_METADATA* aData, bool& aClearPreviousAttributes,
  441. bool aUseX1StructuredComment )
  442. {
  443. aClearPreviousAttributes = false;
  444. wxString prepend_string;
  445. wxString eol_string;
  446. if( aUseX1StructuredComment )
  447. {
  448. prepend_string = "G04 #@! ";
  449. eol_string = "*\n";
  450. }
  451. else
  452. {
  453. prepend_string = "%";
  454. eol_string = "*%\n";
  455. }
  456. // print a Gerber net attribute record.
  457. // it is added to the object attributes dictionary
  458. // On file, only modified or new attributes are printed.
  459. if( aData == nullptr )
  460. return false;
  461. std::string pad_attribute_string;
  462. std::string net_attribute_string;
  463. std::string cmp_attribute_string;
  464. if( aData->m_NetAttribType == GBR_NETLIST_METADATA::GBR_NETINFO_UNSPECIFIED )
  465. return false; // idle command: do nothing
  466. if( ( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_PAD ) )
  467. {
  468. // print info associated to a flashed pad (cmpref, pad name, and optionally pin function)
  469. // example1: %TO.P,R5,3*%
  470. // example2: %TO.P,R5,3,reset*%
  471. pad_attribute_string = prepend_string + "TO.P,";
  472. pad_attribute_string += FormatStringToGerber( aData->m_Cmpref ) + ",";
  473. if( aData->m_Padname.IsEmpty() )
  474. {
  475. // Happens for "mechanical" or never connected pads
  476. pad_attribute_string += FormatStringToGerber( NO_PAD_NAME );
  477. }
  478. else
  479. {
  480. pad_attribute_string += aData->m_Padname.GetGerberString();
  481. // In Pcbnew, the pin function comes from the schematic.
  482. // so it exists only for named pads
  483. if( !aData->m_PadPinFunction.IsEmpty() )
  484. {
  485. pad_attribute_string += ',';
  486. pad_attribute_string += aData->m_PadPinFunction.GetGerberString();
  487. }
  488. }
  489. pad_attribute_string += eol_string;
  490. }
  491. if( ( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_NET ) )
  492. {
  493. // print info associated to a net
  494. // example: %TO.N,Clk3*%
  495. net_attribute_string = prepend_string + "TO.N,";
  496. if( aData->m_Netname.IsEmpty() )
  497. {
  498. if( aData->m_NotInNet )
  499. {
  500. // Happens for not connectable pads: mechanical pads
  501. // and pads with no padname/num
  502. // In this case the net name must be left empty
  503. }
  504. else
  505. {
  506. // Happens for not connected pads: use a normalized
  507. // dummy name
  508. net_attribute_string += FormatStringToGerber( NO_NET_NAME );
  509. }
  510. }
  511. else
  512. {
  513. net_attribute_string += FormatStringToGerber( aData->m_Netname );
  514. }
  515. net_attribute_string += eol_string;
  516. }
  517. if( ( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_CMP ) &&
  518. !( aData->m_NetAttribType & GBR_NETLIST_METADATA::GBR_NETINFO_PAD ) )
  519. {
  520. // print info associated to a footprint
  521. // example: %TO.C,R2*%
  522. // Because GBR_NETINFO_PAD option already contains this info, it is not
  523. // created here for a GBR_NETINFO_PAD attribute
  524. cmp_attribute_string = prepend_string + "TO.C,";
  525. cmp_attribute_string += FormatStringToGerber( aData->m_Cmpref ) + eol_string;
  526. }
  527. // the full list of requested attributes:
  528. std::string full_attribute_string = pad_attribute_string + net_attribute_string
  529. + cmp_attribute_string;
  530. // the short list of requested attributes
  531. // (only modified or new attributes are stored here):
  532. std::string short_attribute_string;
  533. // Attributes have changed: update attribute string, and see if the previous attribute
  534. // list (dictionary in Gerber language) must be cleared
  535. if( aLastNetAttributes != full_attribute_string )
  536. {
  537. // first, remove no longer existing attributes.
  538. // Because in KiCad the full attribute list is evaluated for each object,
  539. // the entire dictionary is cleared
  540. // If m_TryKeepPreviousAttributes is true, only the no longer existing attribute
  541. // is cleared.
  542. // Note: to avoid interaction between clear attributes and set attributes
  543. // the clear attribute is inserted first.
  544. bool clearDict = false;
  545. if( aLastNetAttributes.find( "TO.P," ) != std::string::npos )
  546. {
  547. if( pad_attribute_string.empty() ) // No more this attribute
  548. {
  549. if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
  550. short_attribute_string.insert( 0, prepend_string + "TO.P" + eol_string );
  551. else
  552. clearDict = true;
  553. }
  554. else if( aLastNetAttributes.find( pad_attribute_string ) == std::string::npos )
  555. {
  556. // This attribute has changed
  557. short_attribute_string += pad_attribute_string;
  558. }
  559. }
  560. else // New attribute
  561. {
  562. short_attribute_string += pad_attribute_string;
  563. }
  564. if( aLastNetAttributes.find( "TO.N," ) != std::string::npos )
  565. {
  566. if( net_attribute_string.empty() ) // No more this attribute
  567. {
  568. if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
  569. short_attribute_string.insert( 0, prepend_string + "TO.N" + eol_string );
  570. else
  571. clearDict = true;
  572. }
  573. else if( aLastNetAttributes.find( net_attribute_string ) == std::string::npos )
  574. {
  575. // This attribute has changed.
  576. short_attribute_string += net_attribute_string;
  577. }
  578. }
  579. else // New attribute
  580. {
  581. short_attribute_string += net_attribute_string;
  582. }
  583. if( aLastNetAttributes.find( "TO.C," ) != std::string::npos )
  584. {
  585. if( cmp_attribute_string.empty() ) // No more this attribute
  586. {
  587. if( aData->m_TryKeepPreviousAttributes ) // Clear only this attribute
  588. {
  589. // Refinement:
  590. // the attribute will be cleared only if there is no pad attribute.
  591. // If a pad attribute exists, the component name exists so the old
  592. // TO.C value will be updated, therefore no need to clear it before updating
  593. if( pad_attribute_string.empty() )
  594. short_attribute_string.insert( 0, prepend_string + "TO.C" + eol_string );
  595. }
  596. else
  597. {
  598. clearDict = true;
  599. }
  600. }
  601. else if( aLastNetAttributes.find( cmp_attribute_string ) == std::string::npos )
  602. {
  603. // This attribute has changed.
  604. short_attribute_string += cmp_attribute_string;
  605. }
  606. }
  607. else // New attribute
  608. {
  609. short_attribute_string += cmp_attribute_string;
  610. }
  611. aClearPreviousAttributes = clearDict;
  612. aLastNetAttributes = full_attribute_string;
  613. if( clearDict )
  614. aPrintedText = full_attribute_string;
  615. else
  616. aPrintedText = short_attribute_string;
  617. }
  618. return true;
  619. }
  620. void GBR_CMP_PNP_METADATA::ClearData()
  621. {
  622. // Clear all strings
  623. m_Orientation = 0.0;
  624. m_Manufacturer.Clear();
  625. m_MPN.Clear();
  626. m_Package.Clear();
  627. m_Value.Clear();
  628. m_MountType = MOUNT_TYPE_UNSPECIFIED;
  629. }
  630. /**
  631. * @return a string containing the formatted metadata in X2 syntax.
  632. * one line by non empty data
  633. * the orientation (.CRot) and mount type (.CMnt) are always generated
  634. */
  635. wxString GBR_CMP_PNP_METADATA::FormatCmpPnPMetadata()
  636. {
  637. wxString text;
  638. wxString start_of_line( "%TO." );
  639. wxString end_of_line( "*%\n" );
  640. wxString mounType[] =
  641. {
  642. "Other", "SMD", "TH"
  643. };
  644. if( !m_Manufacturer.IsEmpty() )
  645. text << start_of_line << "CMfr," << m_Manufacturer << end_of_line;
  646. if( !m_MPN.IsEmpty() )
  647. text << start_of_line << "CMPN," << m_MPN << end_of_line;
  648. if( !m_Package.IsEmpty() )
  649. text << start_of_line << "Cpkg," << m_Package << end_of_line;
  650. if( !m_Footprint.IsEmpty() )
  651. text << start_of_line << "CFtp," << m_Footprint << end_of_line;
  652. if( !m_Value.IsEmpty() )
  653. text << start_of_line << "CVal," << m_Value << end_of_line;
  654. if( !m_LibraryName.IsEmpty() )
  655. text << start_of_line << "CLbN," << m_LibraryName << end_of_line;
  656. if( !m_LibraryDescr.IsEmpty() )
  657. text << start_of_line << "CLbD," << m_LibraryDescr << end_of_line;
  658. text << start_of_line << "CMnt," << mounType[m_MountType] << end_of_line;
  659. text << start_of_line << "CRot," << m_Orientation << end_of_line;
  660. return text;
  661. }