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.

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