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.

797 lines
28 KiB

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