From 4daaaa373ce06431bbcf8187ed1151f07d8edaa6 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Mon, 29 Oct 2018 17:23:35 +0100 Subject: [PATCH] Pcbnew, Gerber output: fix incorrect UUID format in %TF.ProjectId attribute. Fixes: lp:1800459 https://bugs.launchpad.net/kicad/+bug/1800459 --- pcbnew/pcbplot.cpp | 171 ++++++++++++++++++--------------------------- 1 file changed, 69 insertions(+), 102 deletions(-) diff --git a/pcbnew/pcbplot.cpp b/pcbnew/pcbplot.cpp index 5eb76da21d..63dddf13df 100644 --- a/pcbnew/pcbplot.cpp +++ b/pcbnew/pcbplot.cpp @@ -1,9 +1,9 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -276,99 +276,82 @@ static wxString& makeStringCompatX1( wxString& aText, bool aUseX1CompatibilityMo } -void BuildGerberX2Header( const BOARD *aBoard, wxArrayString& aHeader ) +/** A helper function to build a project GUID using format RFC4122 Version 1 or 4 + * from the project name, because a kicad project has no specific GUID + * RFC4122 is used mainly for its syntax, because fields have no meaning for Gerber files + * and therefore the GUID generated has no meaning because it do not use any time and time stamp + * specific to the project, just a random pattern (random is here a pattern specific to a project). + * + * See en.wikipedia.org/wiki/Universally_unique_identifier + */ +static wxString makeProjectGUIDfromString( wxString& aText ) { - wxString text; + // Gerber GUID format should be RFC4122 Version 1 or 4. The format is: + // xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx + // with + // x = hexDigit lower/upper case + // and + // M = '1' or '4' (UUID version: 1 (basic) or 4 (random)) (we use 4: UUID random) + // and + // N = '8' or '9' or 'A|a' or 'B|b' : UUID variant 1: 2 MSB bits have meaning) (we use N = 9) + // N = 1000 or 1001 or 1010 or 1011 : 10xx means Variant 1 (Variant2: 110x and 111x are reserved) - // Creates the TF,.GenerationSoftware. Format is: - // %TF,.GenerationSoftware,,[,]*% - text.Printf( wxT( "%%TF.GenerationSoftware,KiCad,Pcbnew,%s*%%" ), GetBuildVersion() ); - aHeader.Add( text ); - - // creates the TF.CreationDate ext: - // The attribute value must conform to the full version of the ISO 8601 - // date and time format, including time and time zone. Note that this is - // the date the Gerber file was effectively created, - // not the time the project of PCB was started - wxDateTime date( wxDateTime::GetTimeNow() ); - // Date format: see http://www.cplusplus.com/reference/ctime/strftime - wxString msg = date.Format( wxT( "%z" ) ); // Extract the time zone offset - // The time zone offset format is + (or -) mm or hhmm (mm = number of minutes, hh = number of hours) - // we want +(or -) hh:mm - if( msg.Len() > 3 ) - msg.insert( 3, ":", 1 ), - text.Printf( wxT( "%%TF.CreationDate,%s%s*%%" ), GetChars( date.FormatISOCombined() ), GetChars( msg ) ); - aHeader.Add( text ); - - // Creates the TF,.ProjectId. Format is (from Gerber file format doc): - // %TF.ProjectId,,,*% - // is the name of the project, restricted to basic ASCII symbols only, - // and comma not accepted - // All illegal chars will be replaced by underscore - // is a 32 hexadecimal digits string which is an unique id of a project. - // This is a random 128-bit number expressed in 32 hexadecimal digits. - // See en.wikipedia.org/wiki/GUID for more information - // However Kicad does not handle such a project GUID, so it is built from the board name - // Rem: accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files). - wxFileName fn = aBoard->GetFileName(); - msg = fn.GetFullName(); wxString guid; // Build a 32 digits GUID from the board name: - for( unsigned ii = 0; ii < msg.Len(); ii++ ) - { - int cc1 = int( msg[ii] ) & 0x0F; - int cc2 = ( int( msg[ii] ) >> 4) & 0x0F; - guid << wxString::Format( wxT( "%X%X" ), cc2, cc1 ); - - if( guid.Len() >= 32 ) - break; - } - - // guid has 32 digits, so add missing digits - int cnt = 32 - guid.Len(); + // guid has 32 digits, so add chars in name to be sure we can build a 32 digits guid + // (i.e. from a 16 char string name) + // In fact only 30 digits are used, and 2 UID id + wxString bname = aText; + int cnt = 16 - bname.Len(); if( cnt > 0 ) - guid.Append( '0', cnt ); + bname.Append( 'X', cnt ); - // build the string: this is the board short filename (without ext) - // and all non ASCII chars and comma are replaced by '_' - msg = fn.GetName(); - msg.Replace( wxT( "," ), wxT( "_" ) ); + int chr_idx = 0; - // build the string. All non ASCII chars and comma are replaced by '_' - wxString rev = ((BOARD*)aBoard)->GetTitleBlock().GetRevision(); - rev.Replace( wxT( "," ), wxT( "_" ) ); + // Output the 8 first hex digits: + for( unsigned ii = 0; ii < 4; ii++ ) + { + int cc = int( bname[chr_idx++] ) & 0xFF; + guid << wxString::Format( "%2.2x", cc ); + } - if( rev.IsEmpty() ) - rev = wxT( "rev?" ); + // Output the 4 next hex digits: + guid << '-'; - text.Printf( wxT( "%%TF.ProjectId,%s,%s,%s*%%" ), msg.ToAscii(), GetChars( guid ), rev.ToAscii() ); - aHeader.Add( text ); + for( unsigned ii = 0; ii < 2; ii++ ) + { + int cc = int( bname[chr_idx++] ) & 0xFF; + guid << wxString::Format( "%2.2x", cc ); + } - // Add the TF.SameCoordinates, that specify all gerber files uses the same - // origin and orientation, and the registration between files is OK. - // The parameter of TF.SameCoordinates is a string that is common - // to all files using the same registration and has no special meaning: - // this is just a key - // Because there is no mirroring/rotation in Kicad, only the plot offset origin - // can create incorrect registration. - // So we create a key from plot offset options. - // and therefore for a given board, all Gerber files having the same key have the same - // plot origin and use the same registration - // - // Currently the key is "Original" when using absolute Pcbnew coordinates, - // and te PY ans PY position od auxiliary axis, when using it. - // Please, if absolute Pcbnew coordinates, one day, are set by user, change the way - // the key is built to ensure file only using the *same* axis have the same key. - wxString registration_id = "Original"; - wxPoint auxOrigin = aBoard->GetAuxOrigin(); + // Output the 4 next hex digits (UUID version and 3 digits): + guid << "-4"; // first digit: UUID version 4 (M = 4) + { + int cc = int( bname[chr_idx++] ) << 4 & 0xFF0; + cc += int( bname[chr_idx] ) >> 4 & 0x0F; + guid << wxString::Format( "%3.3x", cc ); + } - if( aBoard->GetPlotOptions().GetUseAuxOrigin() && auxOrigin.x && auxOrigin.y ) - registration_id.Printf( "PX%xPY%x", auxOrigin.x, auxOrigin.y ); + // Output the 4 next hex digits (UUID variant and 3 digits): + guid << "-9"; // first digit: UUID variant 1 (N = 9) + { + int cc = (int( bname[chr_idx++] ) & 0x0F) << 8; + cc += int( bname[chr_idx++] ) & 0xFF; + guid << wxString::Format( "%3.3x", cc ); + } - text.Printf( "%%TF.SameCoordinates,%s*%%", registration_id.GetData() ); - aHeader.Add( text ); + // Output the 12 last hex digits: + guid << '-'; + + for( unsigned ii = 0; ii < 6; ii++ ) + { + int cc = int( bname[chr_idx++] ) & 0xFF; + guid << wxString::Format( "%2.2x", cc ); + } + + return guid; } @@ -400,33 +383,17 @@ void AddGerberX2Header( PLOTTER * aPlotter, // Creates the TF,.ProjectId. Format is (from Gerber file format doc): // %TF.ProjectId,,,*% // is the name of the project, restricted to basic ASCII symbols only, + // Rem: accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files). // and comma not accepted // All illegal chars will be replaced by underscore - // is a 32 hexadecimal digits string which is an unique id of a project. - // This is a random 128-bit number expressed in 32 hexadecimal digits. - // See en.wikipedia.org/wiki/GUID for more information + // + // is a string which is an unique id of a project. // However Kicad does not handle such a project GUID, so it is built from the board name - // Rem: accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files). wxFileName fn = aBoard->GetFileName(); msg = fn.GetFullName(); - wxString guid; - // Build a 32 digits GUID from the board name: - for( unsigned ii = 0; ii < msg.Len(); ii++ ) - { - int cc1 = int( msg[ii] ) & 0x0F; - int cc2 = ( int( msg[ii] ) >> 4) & 0x0F; - guid << wxString::Format( wxT( "%X%X" ), cc2, cc1 ); - - if( guid.Len() >= 32 ) - break; - } - - // guid has 32 digits, so add missing digits - int cnt = 32 - guid.Len(); - - if( cnt > 0 ) - guid.Append( '0', cnt ); + // Build a , from the board name + wxString guid = makeProjectGUIDfromString( msg ); // build the string: this is the board short filename (without ext) // and all non ASCII chars and comma are replaced by '_'