From 7a110d0ce3e70ef4ae208412a411fb7866396dfd Mon Sep 17 00:00:00 2001
From: unknown <cirilo_bernardo@yahoo.com>
Date: Thu, 5 Jun 2014 20:37:04 +0200
Subject: [PATCH] IDF tools: code cleanup and debugging

---
 pcbnew/CMakeLists.txt                      |    6 +-
 pcbnew/dialogs/dialog_export_idf.cpp       |    2 +-
 pcbnew/exporters/export_idf.cpp            |  237 +++-
 pcbnew/exporters/idf.cpp                   | 1443 --------------------
 pcbnew/exporters/idf.h                     |  321 -----
 pcbnew/exporters/idf_common.cpp            |  484 -------
 pcbnew/exporters/idf_common.h              |  290 ----
 utils/idftools/idf_examples/test_donut.emn |   45 +
 utils/idftools/idf_examples/test_donut.emp |    5 +
 utils/idftools/idf_outlines.cpp            |    4 +
 utils/idftools/idf_parser.cpp              |  207 ++-
 utils/idftools/idf_parser.h                |   10 +-
 utils/idftools/vrml_layer.cpp              |   11 -
 13 files changed, 441 insertions(+), 2624 deletions(-)
 delete mode 100644 pcbnew/exporters/idf.cpp
 delete mode 100644 pcbnew/exporters/idf.h
 delete mode 100644 pcbnew/exporters/idf_common.cpp
 delete mode 100644 pcbnew/exporters/idf_common.h
 create mode 100644 utils/idftools/idf_examples/test_donut.emn
 create mode 100644 utils/idftools/idf_examples/test_donut.emp

diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index 2fc30d1fc7..2b71524a1f 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -36,6 +36,7 @@ include_directories(
     ./exporters
     ../lib_dxf
     ./import_dxf
+    ../utils/idftools
     ${INC_AFTER}
     )
 
@@ -136,8 +137,6 @@ set( PCBNEW_EXPORTERS
     exporters/export_gencad.cpp
     exporters/export_idf.cpp
     exporters/export_vrml.cpp
-    exporters/idf_common.cpp
-    exporters/idf.cpp
     exporters/gen_drill_report_files.cpp
     exporters/gen_modules_placefile.cpp
     exporters/gendrill_Excellon_writer.cpp
@@ -385,6 +384,7 @@ if( KICAD_SCRIPTING_MODULES )
         common
         pcad2kicadpcb
         lib_dxf
+        idf3
         ${GITHUB_PLUGIN_LIBRARIES}
         polygon
         bitmaps
@@ -565,6 +565,7 @@ if( USE_KIWAY_DLLS )
         bitmaps
         gal
         lib_dxf
+        idf3
         ${GITHUB_PLUGIN_LIBRARIES}
         ${wxWidgets_LIBRARIES}
         ${OPENGL_LIBRARIES}
@@ -633,6 +634,7 @@ else()  # milestone A) kills this off:
         bitmaps
         gal
         lib_dxf
+        idf3
         ${GITHUB_PLUGIN_LIBRARIES}
         ${wxWidgets_LIBRARIES}
         ${OPENGL_LIBRARIES}
diff --git a/pcbnew/dialogs/dialog_export_idf.cpp b/pcbnew/dialogs/dialog_export_idf.cpp
index 955c5e932a..9e3d14d8c0 100644
--- a/pcbnew/dialogs/dialog_export_idf.cpp
+++ b/pcbnew/dialogs/dialog_export_idf.cpp
@@ -36,7 +36,7 @@
 #define OPTKEY_IDF_THOU wxT( "IDFExportThou" )
 
 
-bool Export_IDF3( BOARD *aPcb, const wxString & aFullFileName, double aUseThou );
+bool Export_IDF3( BOARD *aPcb, const wxString & aFullFileName, bool aUseThou );
 
 
 class DIALOG_EXPORT_IDF3: public DIALOG_EXPORT_IDF3_BASE
diff --git a/pcbnew/exporters/export_idf.cpp b/pcbnew/exporters/export_idf.cpp
index 3e2f3e796e..a236d4a91a 100644
--- a/pcbnew/exporters/export_idf.cpp
+++ b/pcbnew/exporters/export_idf.cpp
@@ -33,8 +33,9 @@
 #include <class_board.h>
 #include <class_module.h>
 #include <class_edge_mod.h>
-#include <idf.h>
+#include <idf_parser.h>
 #include <3d_struct.h>
+#include <build_version.h>
 
 // assumed default graphical line thickness: 10000 IU == 0.1mm
 #define LINE_WIDTH (100000)
@@ -45,15 +46,15 @@
  * the data into a form which can be output as an IDFv3 compliant
  * BOARD_OUTLINE section.
  */
-static void idf_export_outline( BOARD* aPcb, IDF_BOARD& aIDFBoard )
+static void idf_export_outline( BOARD* aPcb, IDF3_BOARD& aIDFBoard )
 {
-    double scale = aIDFBoard.GetScale();
+    double scale = aIDFBoard.GetUserScale();
 
     DRAWSEGMENT* graphic;               // KiCad graphical item
     IDF_POINT sp, ep;                   // start and end points from KiCad item
 
     std::list< IDF_SEGMENT* > lines;    // IDF intermediate form of KiCad graphical item
-    IDF_OUTLINE outline;                // graphical items forming an outline or cutout
+    IDF_OUTLINE* outline = NULL;        // graphical items forming an outline or cutout
 
     // NOTE: IMPLEMENTATION
     // If/when component cutouts are allowed, we must implement them separately. Cutouts
@@ -61,7 +62,7 @@ static void idf_export_outline( BOARD* aPcb, IDF_BOARD& aIDFBoard )
     // The module cutouts should be handled via the idf_export_module() routine.
 
     double offX, offY;
-    aIDFBoard.GetOffset( offX, offY );
+    aIDFBoard.GetUserOffset( offX, offY );
 
     // Retrieve segments and arcs from the board
     for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() )
@@ -129,22 +130,31 @@ static void idf_export_outline( BOARD* aPcb, IDF_BOARD& aIDFBoard )
     // note: we do not use a try/catch block here since we intend
     // to simply ignore unclosed loops and continue processing
     // until we're out of segments to process
-    IDF3::GetOutline( lines, outline );
+    outline = new IDF_OUTLINE;
+    IDF3::GetOutline( lines, *outline );
 
-    if( outline.empty() )
+    if( outline->empty() )
         goto UseBoundingBox;
 
-    aIDFBoard.AddOutline( outline );
+    aIDFBoard.AddBoardOutline( outline );
+    outline = NULL;
 
     // get all cutouts and write them out
     while( !lines.empty() )
     {
-        IDF3::GetOutline( lines, outline );
+        if( !outline )
+            outline = new IDF_OUTLINE;
 
-        if( outline.empty() )
+        IDF3::GetOutline( lines, *outline );
+
+        if( outline->empty() )
+        {
+            outline->Clear();
             continue;
+        }
 
-        aIDFBoard.AddOutline( outline );
+        aIDFBoard.AddBoardOutline( outline );
+        outline = NULL;
     }
 
     return;
@@ -158,7 +168,10 @@ UseBoundingBox:
         lines.pop_front();
     }
 
-    outline.Clear();
+    if( outline )
+        outline->Clear();
+    else
+        outline = new IDF_OUTLINE;
 
     // fetch a rectangular bounding box for the board;
     // there is always some uncertainty in the board dimensions
@@ -192,7 +205,7 @@ UseBoundingBox:
     p2.x    = px[0];
     p2.y    = py[0];
 
-    outline.push( new IDF_SEGMENT( p1, p2 ) );
+    outline->push( new IDF_SEGMENT( p1, p2 ) );
 
     for( int i = 1; i < 4; ++i )
     {
@@ -201,10 +214,10 @@ UseBoundingBox:
         p2.x    = px[i];
         p2.y    = py[i];
 
-        outline.push( new IDF_SEGMENT( p1, p2 ) );
+        outline->push( new IDF_SEGMENT( p1, p2 ) );
     }
 
-    aIDFBoard.AddOutline( outline );
+    aIDFBoard.AddBoardOutline( outline );
 }
 
 
@@ -216,7 +229,7 @@ UseBoundingBox:
  * the library ELECTRICAL section.
  */
 static void idf_export_module( BOARD* aPcb, MODULE* aModule,
-        IDF_BOARD& aIDFBoard )
+        IDF3_BOARD& aIDFBoard )
 {
     // Reference Designator
     std::string crefdes = TO_UTF8( aModule->GetReference() );
@@ -243,14 +256,14 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
 
     // Export pads
     double  drill, x, y;
-    double  scale = aIDFBoard.GetScale();
+    double  scale = aIDFBoard.GetUserScale();
     IDF3::KEY_PLATING kplate;
     std::string pintype;
     std::string tstr;
 
     double dx, dy;
 
-    aIDFBoard.GetOffset( dx, dy );
+    aIDFBoard.GetUserOffset( dx, dy );
 
     for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
     {
@@ -313,7 +326,19 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
             }
             else
             {
-                aIDFBoard.AddDrill( drill, x, y, kplate, crefdes, pintype, IDF3::ECAD );
+                IDF_DRILL_DATA *dp = new IDF_DRILL_DATA( drill, x, y, kplate, crefdes,
+                                                         pintype, IDF3::ECAD );
+
+                if( !aIDFBoard.AddDrill( dp ) )
+                {
+                    delete dp;
+
+                    std::ostringstream ostr;
+                    ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__;
+                    ostr << "(): could not add drill";
+
+                    throw std::runtime_error( ostr.str() );
+                }
             }
         }
     }
@@ -321,6 +346,8 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
     // add any valid models to the library item list
     std::string refdes;
 
+    IDF3_COMPONENT* comp = NULL;
+
     for( S3D_MASTER* modfile = aModule->Models(); modfile != 0; modfile = modfile->Next() )
     {
         if( !modfile->Is3DType( S3D_MASTER::FILE3D_IDF ) )
@@ -330,14 +357,26 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
         {
             refdes = TO_UTF8( aModule->GetReference() );
 
+            // NOREFDES cannot be used or else the software gets confused
+            // when writing out the placement data due to conflicting
+            // placement and layer specifications; to work around this we
+            // create a (hopefully) unique refdes for our exported part.
             if( refdes.empty() || !refdes.compare( "~" ) )
-                refdes = aIDFBoard.GetRefDes();
+                refdes = aIDFBoard.GetNewRefDes();
         }
 
+        IDF3_COMP_OUTLINE* outline;
+
+        outline = aIDFBoard.GetComponentOutline( modfile->GetShape3DName() );
+
+        if( !outline )
+            throw( std::runtime_error( aIDFBoard.GetError() ) );
+
         double rotz = aModule->GetOrientation()/10.0;
         double locx = modfile->m_MatPosition.x;
         double locy = modfile->m_MatPosition.y;
         double locz = modfile->m_MatPosition.z;
+        double lrot = modfile->m_MatRotation.z;
 
         bool top = ( aModule->GetLayer() == LAYER_N_BACK ) ? false : true;
 
@@ -348,12 +387,12 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
             RotatePoint( &locx, &locy, aModule->GetOrientation() );
             locy = -locy;
         }
+
         if( !top )
         {
             RotatePoint( &locx, &locy, aModule->GetOrientation() );
             locy = -locy;
 
-            rotz -= modfile->m_MatRotation.z;
             rotz = 180.0 - rotz;
 
             if( rotz >= 360.0 )
@@ -363,10 +402,97 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
                 while( rotz <= -360.0 ) rotz += 360.0;
         }
 
-        locx += aModule->GetPosition().x * scale + dx;
-        locy += -aModule->GetPosition().y * scale + dy;
+        if( comp == NULL )
+            comp = aIDFBoard.FindComponent( refdes );
 
-        aIDFBoard.PlaceComponent( modfile->GetShape3DName(), refdes, locx, locy, locz, rotz, top );
+        if( comp == NULL )
+        {
+            comp = new IDF3_COMPONENT( &aIDFBoard );
+
+            if( comp == NULL )
+                throw( std::runtime_error( aIDFBoard.GetError() ) );
+
+            comp->SetRefDes( refdes );
+
+            if( top )
+                comp->SetPosition( aModule->GetPosition().x * scale + dx,
+                                   -aModule->GetPosition().y * scale + dy,
+                                   rotz, IDF3::LYR_TOP );
+            else
+                comp->SetPosition( aModule->GetPosition().x * scale + dx,
+                                   -aModule->GetPosition().y * scale + dy,
+                                   rotz, IDF3::LYR_BOTTOM );
+
+            comp->SetPlacement( IDF3::PS_ECAD );
+
+            aIDFBoard.AddComponent( comp );
+        }
+        else
+        {
+            double refX, refY, refA;
+            IDF3::IDF_LAYER side;
+
+            if( ! comp->GetPosition( refX, refY, refA, side ) )
+            {
+                // place the item
+                if( top )
+                    comp->SetPosition( aModule->GetPosition().x * scale + dx,
+                                       -aModule->GetPosition().y * scale + dy,
+                                       rotz, IDF3::LYR_TOP );
+                    else
+                        comp->SetPosition( aModule->GetPosition().x * scale + dx,
+                                           -aModule->GetPosition().y * scale + dy,
+                                           rotz, IDF3::LYR_BOTTOM );
+            }
+            else
+            {
+                // check that the retrieved component matches this one
+                refX = refX - ( aModule->GetPosition().x * scale + dx );
+                refY = refY - ( -aModule->GetPosition().y * scale + dy );
+                refA = refA - rotz;
+                refA *= refA;
+                refX *= refX;
+                refY *= refY;
+                refX += refY;
+
+                // conditions: same side, X,Y coordinates within 10 microns,
+                // angle within 0.01 degree
+                if( ( top && side == IDF3::LYR_BOTTOM ) || ( !top && side == IDF3::LYR_TOP )
+                    || ( refA > 0.0001 ) || ( refX > 0.0001 ) )
+                {
+                    comp->GetPosition( refX, refY, refA, side );
+
+                    std::ostringstream ostr;
+                    ostr << "* " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+                    ostr << "* conflicting Reference Designator '" << refdes << "'\n";
+                    ostr << "* X loc: " << (aModule->GetPosition().x * scale + dx);
+                    ostr << " vs. " << refX << "\n";
+                    ostr << "* Y loc: " << (-aModule->GetPosition().y * scale + dy);
+                    ostr << " vs. " << refY << "\n";
+                    ostr << "* angle: " << rotz;
+                    ostr << " vs. " << refA << "\n";
+
+                    if( top )
+                        ostr << "* TOP vs. ";
+                    else
+                        ostr << "* BOTTOM vs. ";
+
+                    if( side == IDF3::LYR_TOP )
+                        ostr << "TOP";
+                    else
+                        ostr << "BOTTOM";
+                    
+                    throw( std::runtime_error( ostr.str() ) );
+                }
+            }
+        }
+
+
+        // create the local data ...
+        IDF3_COMP_OUTLINE_DATA* data = new IDF3_COMP_OUTLINE_DATA( comp, outline );
+
+        data->SetOffsets( locx, locy, locz, lrot );
+        comp->AddOutlineData( data );
     }
 
     return;
@@ -378,21 +504,45 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
  * generates IDFv3 compliant board (*.emn) and library (*.emp)
  * files representing the user's PCB design.
  */
-bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, double aUseThou )
+bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, bool aUseThou )
 {
-    IDF_BOARD idfBoard;
+    IDF3_BOARD idfBoard( IDF3::CAD_ELEC );
 
     SetLocaleTo_C_standard();
 
+    bool ok = true;
+    double scale = 1e-6;    // we must scale internal units to mm for IDF
+    IDF3::IDF_UNIT idfUnit;
+
+    if( aUseThou )
+    {
+        idfUnit = IDF3::UNIT_THOU;
+        idfBoard.SetUserPrecision( 1 );
+    }
+    else
+    {
+        idfUnit = IDF3::UNIT_MM;
+        idfBoard.SetUserPrecision( 5 );
+    }
+
+    wxFileName brdName = aPcb->GetFileName();
+
+    idfBoard.SetUserScale( scale );
+    idfBoard.SetBoardThickness( aPcb->GetDesignSettings().GetBoardThickness() * scale );
+    idfBoard.SetBoardName( TO_UTF8( brdName.GetFullName() ) );
+    idfBoard.SetBoardVersion( 0 );
+    idfBoard.SetLibraryVersion( 0 );
+
+    std::ostringstream ostr;
+    ostr << "Created by KiCad " << TO_UTF8( GetBuildVersion() );
+    idfBoard.SetIDFSource( ostr.str() );
+
     try
     {
-        idfBoard.Setup( aPcb->GetFileName(), aFullFileName, aUseThou,
-                        aPcb->GetDesignSettings().GetBoardThickness() );
-
         // set up the global offsets
         EDA_RECT bbox = aPcb->ComputeBoundingBox( true );
-        idfBoard.SetOffset( -bbox.Centre().x * idfBoard.GetScale(),
-                            bbox.Centre().y * idfBoard.GetScale() );
+        idfBoard.SetUserOffset( -bbox.Centre().x * scale,
+                            bbox.Centre().y * scale );
 
         // Export the board outline
         idf_export_outline( aPcb, idfBoard );
@@ -401,15 +551,32 @@ bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, double aUseThou )
         for( MODULE* module = aPcb->m_Modules; module != 0; module = module->Next() )
             idf_export_module( aPcb, module, idfBoard );
 
-        idfBoard.Finish();
+        if( !idfBoard.WriteFile( aFullFileName, idfUnit, false ) )
+        {
+            wxString msg;
+            msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( idfBoard.GetError().c_str() );
+            wxMessageBox( msg );
+
+            ok = false;
+        }
     }
     catch( const IO_ERROR& ioe )
     {
-        wxLogDebug( wxT( "An error occurred attemping export to IDFv3.\n\nError: %s" ),
-                    GetChars( ioe.errorText ) );
+        wxString msg;
+        msg << _( "IDF Export Failed:\n" ) << ioe.errorText;
+        wxMessageBox( msg );
+
+        ok = false;
+    }
+    catch( std::exception& e )
+    {
+        wxString msg;
+        msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
+        wxMessageBox( msg );
+        ok = false;
     }
 
     SetLocaleTo_Default();
 
-    return true;
+    return ok;
 }
diff --git a/pcbnew/exporters/idf.cpp b/pcbnew/exporters/idf.cpp
deleted file mode 100644
index 69d2487c65..0000000000
--- a/pcbnew/exporters/idf.cpp
+++ /dev/null
@@ -1,1443 +0,0 @@
-/**
- * file: idf.cpp
- *
- * This program source code file is part of KiCad, a free EDA CAD application.
- *
- * Copyright (C) 2013-2014  Cirilo Bernardo
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, you may find one here:
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- * or you may search the http://www.gnu.org website for the version 2 license,
- * or you may write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
- */
-
-// TODO: Consider using different precision formats for THOU vs MM output
-// Keep in mind that THOU cannot represent MM very well although MM can
-// represent 1 THOU with 4 decimal places. For modern manufacturing we
-// are interested in a resolution of about 0.1 THOU.
-
-#include <list>
-#include <string>
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include <algorithm>
-#include <cstdio>
-#include <cmath>
-#include <ctime>
-#include <cctype>
-#include <strings.h>
-#include <pgm_base.h>
-#include <wx/config.h>
-#include <wx/file.h>
-#include <wx/filename.h>
-#include <macros.h>
-#include <richio.h>
-#include <idf.h>
-#include <build_version.h>
-
-// minimum drill diameter (nanometers) - 10000 is a 0.01mm drill
-#define IDF_MIN_DIA ( 10000.0 )
-
-// minimum board thickness; this is about 0.012mm (0.5 mils)
-// which is about the thickness of a single kapton layer typically
-// used in a flexible design.
-#define IDF_MIN_BRD_THICKNESS (12000)
-
-
-// START: a few routines to help IDF_LIB but which may be of general use in the future
-// as IDF support develops
-
-// fetch a line from the given input file and trim the ends
-static bool FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment );
-
-// extract an IDF string and move the index to point to the character after the substring
-static bool GetIDFString( const std::string& aLine, std::string& aIDFString,
-                          bool& hasQuotes, int& aIndex );
-
-// END: IDF_LIB helper routines
-
-
-IDF_DRILL_DATA::IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
-        IDF3::KEY_PLATING aPlating,
-        const std::string aRefDes,
-        const std::string aHoleType,
-        IDF3::KEY_OWNER aOwner )
-{
-    if( aDrillDia < 0.3 )
-        dia = 0.3;
-    else
-        dia = aDrillDia;
-
-    x = aPosX;
-    y = aPosY;
-    plating = aPlating;
-
-    if( !aRefDes.compare( "BOARD" ) )
-    {
-        kref = IDF3::BOARD;
-    }
-    else if( aRefDes.empty() || !aRefDes.compare( "NOREFDES" ) )
-    {
-        kref = IDF3::NOREFDES;
-    }
-    else if( !aRefDes.compare( "PANEL" ) )
-    {
-        kref = IDF3::PANEL;
-    }
-    else
-    {
-        kref = IDF3::REFDES;
-        refdes = aRefDes;
-    }
-
-    if( !aHoleType.compare( "PIN" ) )
-    {
-        khole = IDF3::PIN;
-    }
-    else if( !aHoleType.compare( "VIA" ) )
-    {
-        khole = IDF3::VIA;
-    }
-    else if( aHoleType.empty() || !aHoleType.compare( "MTG" ) )
-    {
-        khole = IDF3::MTG;
-    }
-    else if( !aHoleType.compare( "TOOL" ) )
-    {
-        khole = IDF3::TOOL;
-    }
-    else
-    {
-        khole = IDF3::OTHER;
-        holetype = aHoleType;
-    }
-
-    owner = aOwner;
-}    // IDF_DRILL_DATA::IDF_DRILL_DATA( ... )
-
-
-bool IDF_DRILL_DATA::Write( FILE* aLayoutFile )
-{
-    // TODO: check stream integrity and return 'false' as appropriate
-
-    if( !aLayoutFile )
-        return false;
-
-    std::string holestr;
-    std::string refstr;
-    std::string ownstr;
-    std::string pltstr;
-
-    switch( khole )
-    {
-    case IDF3::PIN:
-        holestr = "PIN";
-        break;
-
-    case IDF3::VIA:
-        holestr = "VIA";
-        break;
-
-    case IDF3::TOOL:
-        holestr = "TOOL";
-        break;
-
-    case IDF3::OTHER:
-        holestr = "\"" + holetype + "\"";
-        break;
-
-    default:
-        holestr = "MTG";
-        break;
-    }
-
-    switch( kref )
-    {
-    case IDF3::BOARD:
-        refstr = "BOARD";
-        break;
-
-    case IDF3::PANEL:
-        refstr = "PANEL";
-        break;
-
-    case IDF3::REFDES:
-        refstr = "\"" + refdes + "\"";
-        break;
-
-    default:
-        refstr = "NOREFDES";
-        break;
-    }
-
-    if( plating == IDF3::PTH )
-        pltstr = "PTH";
-    else
-        pltstr = "NPTH";
-
-    switch( owner )
-    {
-    case IDF3::MCAD:
-        ownstr = "MCAD";
-        break;
-
-    case IDF3::ECAD:
-        ownstr = "ECAD";
-        break;
-
-    default:
-        ownstr = "UNOWNED";
-    }
-
-    fprintf( aLayoutFile, "%.3f %.5f %.5f %s %s %s %s\n",
-            dia, x, y, pltstr.c_str(), refstr.c_str(), holestr.c_str(), ownstr.c_str() );
-
-    return true;
-}    // IDF_DRILL_DATA::Write( aLayoutFile )
-
-
-IDF_BOARD::IDF_BOARD()
-{
-    refdesIndex = 0;
-    outlineIndex = 0;
-    scale = 1e-6;
-    boardThickness = 1.6;       // default to 1.6mm thick boards
-
-    useThou = false;            // by default we want mm output
-    hasBrdOutlineHdr = false;
-
-    layoutFile = NULL;
-    libFile = NULL;
-}
-
-
-IDF_BOARD::~IDF_BOARD()
-{
-    // simply close files if they are open; do not attempt
-    // anything else since a previous exception may have left
-    // data in a bad state.
-    if( layoutFile != NULL )
-    {
-        fclose( layoutFile );
-        layoutFile = NULL;
-    }
-
-    if( libFile != NULL )
-    {
-        fclose( libFile );
-        libFile = NULL;
-    }
-}
-
-
-bool IDF_BOARD::Setup( wxString aBoardName,
-        wxString aFullFileName,
-        bool aUseThou,
-        int aBoardThickness )
-{
-    if( aBoardThickness < IDF_MIN_BRD_THICKNESS )
-        return false;
-
-    if( aUseThou )
-    {
-        useThou = true;
-        scale = 1e-3 / 25.4;
-    }
-    else
-    {
-        useThou = false;
-        scale = 1e-6;
-    }
-
-    boardThickness = aBoardThickness * scale;
-
-    wxFileName brdname( aBoardName );
-    wxFileName idfname( aFullFileName );
-
-    // open the layout file
-    idfname.SetExt( wxT( "emn" ) );
-    layoutFile = wxFopen( aFullFileName, wxT( "wt" ) );
-
-    if( layoutFile == NULL )
-        return false;
-
-    // open the library file
-    idfname.SetExt( wxT( "emp" ) );
-    libFile = wxFopen( idfname.GetFullPath(), wxT( "wt" ) );
-
-    if( libFile == NULL )
-    {
-        fclose( layoutFile );
-        layoutFile = NULL;
-        return false;
-    }
-
-    wxDateTime tdate( time( NULL ) );
-
-    fprintf( layoutFile, ".HEADER\n"
-                         "BOARD_FILE 3.0 \"Created by KiCad %s\""
-                         " %.4u/%.2u/%.2u.%.2u:%.2u:%.2u 1\n"
-                         "\"%s\" %s\n"
-                         ".END_HEADER\n\n",
-            TO_UTF8( GetBuildVersion() ),
-            tdate.GetYear(), tdate.GetMonth() + 1, tdate.GetDay(),
-            tdate.GetHour(), tdate.GetMinute(), tdate.GetSecond(),
-            TO_UTF8( brdname.GetFullName() ), useThou ? "THOU" : "MM" );
-
-    fprintf( libFile, ".HEADER\n"
-                      "LIBRARY_FILE 3.0 \"Created by KiCad %s\" %.4d/%.2d/%.2d.%.2d:%.2d:%.2d 1\n"
-                      ".END_HEADER\n\n",
-            TO_UTF8( GetBuildVersion() ),
-            tdate.GetYear(), tdate.GetMonth() + 1, tdate.GetDay(),
-            tdate.GetHour(), tdate.GetMinute(), tdate.GetSecond() );
-
-    return true;
-}
-
-
-bool IDF_BOARD::Finish( void )
-{
-    // Steps to finalize the board and library files:
-    // 1. (emn) close the BOARD_OUTLINE section
-    // 2. (emn) write out the DRILLED_HOLES section
-    // 3. (emp) finalize the library file
-    // 4. (emn) write out the COMPONENT_PLACEMENT section
-
-    if( layoutFile == NULL || libFile == NULL )
-        return false;
-
-    // Finalize the board outline section
-    fprintf( layoutFile, ".END_BOARD_OUTLINE\n\n" );
-
-    // Write out the drill section
-    bool ok = WriteDrills();
-
-    // populate the library (*.emp) file and write the
-    // PLACEMENT section
-    if( ok )
-        ok = IDFLib.WriteFiles( layoutFile, libFile );
-
-    fclose( libFile );
-    libFile = NULL;
-
-    fclose( layoutFile );
-    layoutFile = NULL;
-
-    return ok;
-}
-
-
-bool IDF_BOARD::AddOutline( IDF_OUTLINE& aOutline )
-{
-    if( !layoutFile )
-        return false;
-
-    // TODO: check the stream integrity
-
-    std::list<IDF_SEGMENT*>::iterator bo;
-    std::list<IDF_SEGMENT*>::iterator eo;
-
-    if( !hasBrdOutlineHdr )
-    {
-        fprintf( layoutFile, ".BOARD_OUTLINE ECAD\n%.5f\n", boardThickness );
-        hasBrdOutlineHdr = true;
-    }
-
-    if( aOutline.size() == 1 )
-    {
-        if( !aOutline.front()->IsCircle() )
-            return false;                   // this is a bad outline
-
-        // NOTE: a circle always has an angle of 360, never -360,
-        // otherwise SolidWorks chokes on the file.
-        fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                aOutline.front()->startPoint.x, aOutline.front()->startPoint.y );
-        fprintf( layoutFile, "%d %.5f %.5f 360\n", outlineIndex,
-                aOutline.front()->endPoint.x, aOutline.front()->endPoint.y );
-
-        ++outlineIndex;
-        return true;
-    }
-
-    // ensure that the very last point is the same as the very first point
-    aOutline.back()-> endPoint = aOutline.front()->startPoint;
-
-    // check if we must reverse things
-    if( ( aOutline.IsCCW() && ( outlineIndex > 0 ) )
-        || ( ( !aOutline.IsCCW() ) && ( outlineIndex == 0 ) ) )
-    {
-        eo  = aOutline.begin();
-        bo  = aOutline.end();
-        --bo;
-
-        // for the first item we write out both points
-        if( aOutline.front()->angle < MIN_ANG && aOutline.front()->angle > -MIN_ANG )
-        {
-            fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                    aOutline.front()->endPoint.x, aOutline.front()->endPoint.y );
-            fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                    aOutline.front()->startPoint.x, aOutline.front()->startPoint.y );
-        }
-        else
-        {
-            fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                    aOutline.front()->endPoint.x, aOutline.front()->endPoint.y );
-            fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex,
-                    aOutline.front()->startPoint.x, aOutline.front()->startPoint.y,
-                    -aOutline.front()->angle );
-        }
-
-        // for all other segments we only write out the start point
-        while( bo != eo )
-        {
-            if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
-            {
-                fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                        (*bo)->startPoint.x, (*bo)->startPoint.y );
-            }
-            else
-            {
-                fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex,
-                        (*bo)->startPoint.x, (*bo)->startPoint.y, -(*bo)->angle );
-            }
-
-            --bo;
-        }
-    }
-    else
-    {
-        bo  = aOutline.begin();
-        eo  = aOutline.end();
-
-        // for the first item we write out both points
-        if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
-        {
-            fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                    (*bo)->startPoint.x, (*bo)->startPoint.y );
-            fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                    (*bo)->endPoint.x, (*bo)->endPoint.y );
-        }
-        else
-        {
-            fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                    (*bo)->startPoint.x, (*bo)->startPoint.y );
-            fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex,
-                    (*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle );
-        }
-
-        ++bo;
-
-        // for all other segments we only write out the last point
-        while( bo != eo )
-        {
-            if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
-            {
-                fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex,
-                        (*bo)->endPoint.x, (*bo)->endPoint.y );
-            }
-            else
-            {
-                fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex,
-                        (*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle );
-            }
-
-            ++bo;
-        }
-    }
-
-    ++outlineIndex;
-
-    return true;
-}
-
-
-bool IDF_BOARD::AddDrill( double dia, double x, double y,
-        IDF3::KEY_PLATING plating,
-        const std::string refdes,
-        const std::string holeType,
-        IDF3::KEY_OWNER owner )
-{
-    if( dia < IDF_MIN_DIA * scale )
-        return false;
-
-    IDF_DRILL_DATA* dp = new IDF_DRILL_DATA( dia, x, y, plating, refdes, holeType, owner );
-    drills.push_back( dp );
-
-    return true;
-}
-
-
-bool IDF_BOARD::AddSlot( double aWidth, double aLength, double aOrientation,
-        double aX, double aY )
-{
-    if( aWidth < IDF_MIN_DIA * scale )
-        return false;
-
-    if( aLength < IDF_MIN_DIA * scale )
-        return false;
-
-    IDF_POINT c[2];     // centers
-    IDF_POINT pt[4];
-
-    double a1 = aOrientation / 180.0 * M_PI;
-    double a2 = a1 + M_PI2;
-    double d1 = aLength / 2.0;
-    double d2 = aWidth / 2.0;
-    double sa1 = sin( a1 );
-    double ca1 = cos( a1 );
-    double dsa2 = d2 * sin( a2 );
-    double dca2 = d2 * cos( a2 );
-
-    c[0].x  = aX + d1 * ca1;
-    c[0].y  = aY + d1 * sa1;
-
-    c[1].x  = aX - d1 * ca1;
-    c[1].y  = aY - d1 * sa1;
-
-    pt[0].x = c[0].x - dca2;
-    pt[0].y = c[0].y - dsa2;
-
-    pt[1].x = c[1].x - dca2;
-    pt[1].y = c[1].y - dsa2;
-
-    pt[2].x = c[1].x + dca2;
-    pt[2].y = c[1].y + dsa2;
-
-    pt[3].x = c[0].x + dca2;
-    pt[3].y = c[0].y + dsa2;
-
-    IDF_OUTLINE outline;
-
-    // first straight run
-    IDF_SEGMENT* seg = new IDF_SEGMENT( pt[0], pt[1] );
-    outline.push( seg );
-    // first 180 degree cap
-    seg = new IDF_SEGMENT( c[1], pt[1], -180.0, true );
-    outline.push( seg );
-    // final straight run
-    seg = new IDF_SEGMENT( pt[2], pt[3] );
-    outline.push( seg );
-    // final 180 degree cap
-    seg = new IDF_SEGMENT( c[0], pt[3], -180.0, true );
-    outline.push( seg );
-
-    return AddOutline( outline );
-}
-
-
-bool IDF_BOARD::PlaceComponent( const wxString aComponentFile, const std::string aRefDes,
-                     double aXLoc, double aYLoc, double aZLoc,
-                     double aRotation, bool isOnTop )
-{
-    return IDFLib.PlaceComponent( aComponentFile, aRefDes,
-                                  aXLoc, aYLoc, aZLoc,
-                                  aRotation, isOnTop );
-}
-
-
-std::string IDF_BOARD::GetRefDes( void )
-{
-    std::ostringstream ostr;
-
-    ostr << "NOREFDES_" << refdesIndex++;
-
-    return ostr.str();
-}
-
-
-bool IDF_BOARD::WriteDrills( void )
-{
-    if( !layoutFile )
-        return false;
-
-    // TODO: check the stream integrity and return false as appropriate
-    if( drills.empty() )
-        return true;
-
-    fprintf( layoutFile, ".DRILLED_HOLES\n" );
-
-    std::list<class IDF_DRILL_DATA*>::iterator ds  = drills.begin();
-    std::list<class IDF_DRILL_DATA*>::iterator de  = drills.end();
-
-    while( ds != de )
-    {
-        if( !(*ds)->Write( layoutFile ) )
-            return false;
-
-        ++ds;
-    }
-
-    fprintf( layoutFile, ".END_DRILLED_HOLES\n" );
-
-    return true;
-}
-
-
-double IDF_BOARD::GetScale( void )
-{
-    return scale;
-}
-
-
-void IDF_BOARD::SetOffset( double x, double y )
-{
-    offsetX = x;
-    offsetY = y;
-}
-
-
-void IDF_BOARD::GetOffset( double& x, double& y )
-{
-    x = offsetX;
-    y = offsetY;
-}
-
-
-IDF_LIB::~IDF_LIB()
-{
-    while( !components.empty() )
-    {
-        delete components.back();
-        components.pop_back();
-    }
-}
-
-
-bool IDF_LIB::writeLib( FILE* aLibFile )
-{
-    if( !aLibFile )
-        return false;
-
-    // TODO: check stream integrity and return false as appropriate
-
-    // export models
-    std::list< IDF_COMP* >::const_iterator mbeg = components.begin();
-    std::list< IDF_COMP* >::const_iterator mend = components.end();
-
-    while( mbeg != mend )
-    {
-        if( !(*mbeg)->WriteLib( aLibFile ) )
-            return false;
-        ++mbeg;
-    }
-
-    libWritten = true;
-    return true;
-}
-
-
-bool IDF_LIB::writeBrd( FILE* aLayoutFile )
-{
-    if( !aLayoutFile || !libWritten )
-        return false;
-
-    if( components.empty() )
-        return true;
-
-    // TODO: check stream integrity and return false as appropriate
-
-    // write out the board placement information
-    std::list< IDF_COMP* >::const_iterator mbeg = components.begin();
-    std::list< IDF_COMP* >::const_iterator mend = components.end();
-
-    fprintf( aLayoutFile, "\n.PLACEMENT\n" );
-
-    while( mbeg != mend )
-    {
-        if( !(*mbeg)->WritePlacement( aLayoutFile ) )
-            return false;
-        ++mbeg;
-    }
-
-    fprintf( aLayoutFile, ".END_PLACEMENT\n" );
-
-    return true;
-}
-
-
-bool IDF_LIB::WriteFiles( FILE* aLayoutFile, FILE* aLibFile )
-{
-    if( !aLayoutFile || !aLibFile )
-        return false;
-
-    libWritten = false;
-    regOutlines.clear();
-
-    if( !writeLib( aLibFile ) )
-        return false;
-
-    return writeBrd( aLayoutFile );
-}
-
-
-bool IDF_LIB::RegisterOutline( const std::string aGeomPartString )
-{
-    std::set< std::string >::const_iterator it = regOutlines.find( aGeomPartString );
-
-    if( it != regOutlines.end() )
-        return true;
-
-    regOutlines.insert( aGeomPartString );
-
-    return false;
-}
-
-
-bool IDF_LIB::PlaceComponent( const wxString aComponentFile, const std::string aRefDes,
-                                double aXLoc, double aYLoc, double aZLoc,
-                                double aRotation, bool isOnTop )
-{
-    IDF_COMP* comp = new IDF_COMP( this );
-
-    if( comp == NULL )
-    {
-        std::cerr << "IDF_LIB: *ERROR* could not allocate memory for a component\n";
-        return false;
-    }
-
-    components.push_back( comp );
-
-    if( !comp->PlaceComponent( aComponentFile, aRefDes,
-                                     aXLoc, aYLoc, aZLoc,
-                                     aRotation, isOnTop ) )
-    {
-        std::cerr << "IDF_LIB: file does not exist (or is symlink):\n";
-        std::cerr << "   FILE: " << TO_UTF8( aComponentFile ) << "\n";
-        return false;
-    }
-
-    return true;
-}
-
-
-IDF_COMP::IDF_COMP( IDF_LIB* aParent )
-{
-    parent = aParent;
-}
-
-
-bool IDF_COMP::PlaceComponent( const wxString aComponentFile, const std::string aRefDes,
-                               double aXLoc, double aYLoc, double aZLoc,
-                               double aRotation, bool isOnTop )
-{
-    componentFile = aComponentFile;
-    refdes = aRefDes;
-
-    if( refdes.empty() || !refdes.compare( "~" ) || !refdes.compare( "0" ) )
-        refdes = "NOREFDES";
-
-    loc_x = aXLoc;
-    loc_y = aYLoc;
-    loc_z = aZLoc;
-    rotation = aRotation;
-    top = isOnTop;
-
-    wxString fname = wxExpandEnvVars( aComponentFile );
-
-    if( !wxFileName::FileExists( fname ) )
-        return false;
-
-    componentFile = fname;
-
-    return true;
-}
-
-
-bool IDF_COMP::WritePlacement( FILE* aLayoutFile )
-{
-    if( aLayoutFile == NULL )
-    {
-        std::cerr << "IDF_COMP: *ERROR* WritePlacement() invoked with aLayoutFile = NULL\n";
-        return false;
-    }
-
-    if( parent == NULL )
-    {
-        std::cerr << "IDF_COMP: *ERROR* no valid pointer \n";
-        return false;
-    }
-
-    if( componentFile.empty() )
-    {
-        std::cerr << "IDF_COMP: *BUG* empty componentFile name in WritePlacement()\n";
-        return false;
-    }
-
-    if( geometry.empty() && partno.empty() )
-    {
-        std::cerr << "IDF_COMP: *BUG* geometry and partno strings are empty in WritePlacement()\n";
-        return false;
-    }
-
-    // TODO: monitor stream integrity and respond accordingly
-
-    // PLACEMENT, RECORD 2:
-    fprintf( aLayoutFile, "\"%s\" \"%s\" \"%s\"\n",
-             geometry.c_str(), partno.c_str(), refdes.c_str() );
-
-    // PLACEMENT, RECORD 3:
-    if( rotation >= -MIN_ANG && rotation <= -MIN_ANG )
-    {
-        fprintf( aLayoutFile, "%.6f %.6f %.6f 0 %s ECAD\n",
-                 loc_x, loc_y, loc_z, top ? "TOP" : "BOTTOM" );
-    }
-    else
-    {
-        fprintf( aLayoutFile, "%.6f %.6f %.6f %.3f %s ECAD\n",
-                 loc_x, loc_y, loc_z, rotation, top ? "TOP" : "BOTTOM" );
-    }
-
-    return true;
-}
-
-
-bool IDF_COMP::WriteLib( FILE* aLibFile )
-{
-    // 1. parse the file for the .ELECTRICAL or .MECHANICAL section
-    //      and extract the Geometry and PartNumber strings
-    // 2. Register the name; check if it already exists
-    // 3. parse the rest of the file until .END_ELECTRICAL or
-    //      .END_MECHANICAL; validate that each entry conforms
-    //      to a valid outline
-    // 4. write lines to library file
-    //
-    // NOTE on parsing (the order matters):
-    //  + store each line which begins with '#'
-    //  + strip blanks from both ends of the line
-    //  + drop each blank line
-    //  + the first non-blank non-comment line must be
-    //      .ELECTRICAL or .MECHANICAL (as per spec, case does not matter)
-    //  + the first non-blank line after RECORD 1 must be RECORD 2
-    //  + following RECORD 2, only blank lines, valid outline entries,
-    //      and .END_{MECHANICAL,ELECTRICAL} are allowed
-    //  + only a single outline may be specified; the order may be
-    //      CW or CCW.
-    //  + all valid lines are stored and written to the library file
-    //
-    // return: false if we do could not write model data; we may return
-    //  true even if we could not read an IDF file for some reason, provided
-    //  that the default model was written. In such a case, warnings will be
-    //  written to stderr.
-
-    if( aLibFile == NULL )
-    {
-        std::cerr << "IDF_COMP: *ERROR* WriteLib() invoked with aLibFile = NULL\n";
-        return false;
-    }
-
-    if( parent == NULL )
-    {
-        std::cerr << "IDF_COMP: *ERROR* no valid pointer \n";
-        return false;
-    }
-
-    if( componentFile.empty() )
-    {
-        std::cerr << "IDF_COMP: *BUG* empty componentFile name in WriteLib()\n";
-        return false;
-    }
-
-    std::list< std::string > records;
-    std::ifstream model;
-    std::string fname = TO_UTF8( componentFile );
-
-    model.open( fname.c_str(), std::ios_base::in );
-
-    if( !model.is_open() )
-    {
-        std::cerr << "* IDF EXPORT: could not open file " << fname << "\n";
-        return substituteComponent( aLibFile );
-    }
-
-    std::string entryType;  // will be one of ELECTRICAL or MECHANICAL
-    std::string endMark;    // will be one of .END_ELECTRICAL or .END_MECHANICAL
-    std::string iline;      // the input line
-    int state = 1;
-    bool isComment;         // true if a line just read in is a comment line
-    bool isNewItem = false; // true if the outline is a previously unsaved IDF item
-
-    // some vars for parsing record 3
-    int    loopIdx = -1;        // direction of points in outline (0=CW, 1=CCW, -1=no points yet)
-    double firstX;
-    double firstY;
-    bool   lineClosed = false;  // true when outline has been closed; only one outline is permitted
-
-    while( state )
-    {
-        while( !FetchIDFLine( model, iline, isComment ) && model.good() );
-
-        if( !model.good() )
-        {
-            // this should not happen; we should at least
-            // have encountered the .END_ statement;
-            // however, we shall make a concession if the
-            // last line is an .END_ statement which had
-            // not been correctly terminated
-            if( !endMark.empty() && !strncasecmp( iline.c_str(), endMark.c_str(), 15 ) )
-            {
-                std::cerr << "IDF EXPORT: *WARNING* IDF file is not properly terminated\n";
-                std::cerr << "*     FILE: " << fname << "\n";
-                records.push_back( endMark );
-                break;
-            }
-
-            std::cerr << "IDF EXPORT: *ERROR* faulty IDF file\n";
-            std::cerr << "*     FILE: " << fname << "\n";
-            return substituteComponent( aLibFile );
-        }
-
-        switch( state )
-        {
-            case 1:
-                // accept comment lines, .ELECTRICAL, or .MECHANICAL;
-                // all others are simply ignored
-                if( isComment )
-                {
-                    records.push_back( iline );
-                    break;
-                }
-
-                if( !strncasecmp( iline.c_str(), ".electrical", 11 ) )
-                {
-                    entryType = ".ELECTRICAL";
-                    endMark   = ".END_ELECTRICAL";
-                    records.push_back( entryType );
-                    state = 2;
-                    break;
-                }
-
-                if( !strncasecmp( iline.c_str(), ".mechanical", 11 ) )
-                {
-                    entryType = ".MECHANICAL";
-                    endMark   = ".END_MECHANICAL";
-                    records.push_back( entryType );
-                    state = 2;
-                    break;
-                }
-
-                break;
-
-            case 2:
-                // accept only a RECORD 2 compliant line;
-                // anything else constitutes a malformed IDF file
-                if( isComment )
-                {
-                    std::cerr << "IDF EXPORT: bad IDF file\n";
-                    std::cerr << "*     LINE: " << iline << "\n";
-                    std::cerr << "*     FILE: " << fname << "\n";
-                    std::cerr << "*   REASON: comment within "
-                              << entryType << " section\n";
-                    model.close();
-                    return substituteComponent( aLibFile );
-                }
-
-                if( !parseRec2( iline, isNewItem ) )
-                {
-                    std::cerr << "IDF EXPORT: bad IDF file\n";
-                    std::cerr << "*     LINE: " << iline << "\n";
-                    std::cerr << "*     FILE: " << fname << "\n";
-                    std::cerr << "*   REASON: expecting RECORD 2 of "
-                              << entryType << " section\n";
-                    model.close();
-                    return substituteComponent( aLibFile );
-                }
-
-                if( isNewItem )
-                {
-                    records.push_back( iline );
-                    state = 3;
-                }
-                else
-                {
-                    model.close();
-                    return true;
-                }
-
-                break;
-
-            case 3:
-                // accept outline entries or end of section
-                if( isComment )
-                {
-                    std::cerr << "IDF EXPORT: bad IDF file\n";
-                    std::cerr << "*     LINE: " << iline << "\n";
-                    std::cerr << "*     FILE: " << fname << "\n";
-                    std::cerr << "*   REASON: comment within "
-                              << entryType << " section\n";
-                    model.close();
-                    return substituteComponent( aLibFile );
-                }
-
-                if( !strncasecmp( iline.c_str(), endMark.c_str(), 15 ) )
-                {
-                    records.push_back( endMark );
-                    state = 0;
-                    break;
-                }
-
-                if( lineClosed )
-                {
-                    // there should be no further points
-                    std::cerr << "IDF EXPORT: faulty IDF file\n";
-                    std::cerr << "*     LINE: " << iline << "\n";
-                    std::cerr << "*     FILE: " << fname << "\n";
-                    std::cerr << "*   REASON: more than 1 outline in "
-                              << entryType << " section\n";
-                    model.close();
-                    return substituteComponent( aLibFile );
-                }
-
-                if( !parseRec3( iline, loopIdx, firstX, firstY, lineClosed ) )
-                {
-                    std::cerr << "IDF EXPORT: unexpected line in IDF file\n";
-                    std::cerr << "*     LINE: " << iline << "\n";
-                    std::cerr << "*     FILE: " << fname << "\n";
-                    model.close();
-                    return substituteComponent( aLibFile );
-                }
-
-                records.push_back( iline );
-                break;
-
-            default:
-                std::cerr << "IDF EXPORT: BUG in " << __FUNCTION__ << ": unexpected state\n";
-                model.close();
-                return substituteComponent( aLibFile );
-                break;
-        }   // switch( state )
-    }       // while( state )
-
-    model.close();
-
-    if( !lineClosed )
-    {
-        std::cerr << "IDF EXPORT: component outline not closed\n";
-        std::cerr << "*     FILE: " << fname << "\n";
-        return substituteComponent( aLibFile );
-    }
-
-    std::list< std::string >::iterator lbeg = records.begin();
-    std::list< std::string >::iterator lend = records.end();
-
-    // TODO: check stream integrity
-    while( lbeg != lend )
-    {
-        fprintf( aLibFile, "%s\n", lbeg->c_str() );
-        ++lbeg;
-    }
-    fprintf( aLibFile, "\n" );
-
-    return true;
-}
-
-
-bool IDF_COMP::substituteComponent( FILE* aLibFile )
-{
-    // the component outline does not exist or could not be
-    // read; substitute a placeholder
-
-    // TODO: check the stream integrity
-    geometry = "NOGEOM";
-    partno   = "NOPART";
-
-    if( parent->RegisterOutline( "NOGEOM_NOPART" ) )
-        return true;
-
-    // Create a star shape 5mm high with points on 5 and 2.5 mm circles
-    fprintf( aLibFile, ".ELECTRICAL\n" );
-    fprintf( aLibFile, "\"NOGEOM\" \"NOPART\" MM 5\n" );
-
-    double a, da, x, y;
-    da = M_PI / 5.0;
-    a = da / 2.0;
-
-    for( int i = 0; i < 10; ++i )
-    {
-        if( i & 1 )
-        {
-            x = 2.5 * cos( a );
-            y = 2.5 * sin( a );
-        }
-        else
-        {
-            x = 1.5 * cos( a );
-            y = 1.5 * sin( a );
-        }
-
-        a += da;
-        fprintf( aLibFile, "0 %.3f %.3f 0\n", x, y );
-    }
-
-    a = da / 2.0;
-    x = 1.5 * cos( a );
-    y = 1.5 * sin( a );
-    fprintf( aLibFile, "0 %.3f %.3f 0\n", x, y );
-    fprintf( aLibFile, ".END_ELECTRICAL\n\n" );
-
-    return true;
-}
-
-
-bool IDF_COMP::parseRec2( const std::string aLine, bool& isNewItem )
-{
-    // RECORD 2:
-    // + "Geometry Name"
-    // + "Part Number"
-    // + MM or THOU
-    // + height (float)
-
-    isNewItem = false;
-
-    int idx = 0;
-    bool quoted = false;
-    std::string entry;
-
-    if( !GetIDFString( aLine, entry, quoted, idx ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 2 in model file (no Geometry Name entry)\n";
-        return false;
-    }
-
-    geometry = entry;
-
-    if( !GetIDFString( aLine, entry, quoted, idx ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 2 in model file (no Part No. entry)\n";
-        return false;
-    }
-
-    partno = entry;
-
-    if( geometry.empty() && partno.empty() )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 2 in model file\n";
-        std::cerr << "          Geometry Name and Part Number are both empty.\n";
-        return false;
-    }
-
-    if( !GetIDFString( aLine, entry, quoted, idx ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 2, missing FIELD 3\n";
-        return false;
-    }
-
-    if( strcasecmp( "MM", entry.c_str() ) && strcasecmp( "THOU", entry.c_str() ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 2, invalid FIELD 3 \""
-                  << entry << "\"\n";
-        return false;
-    }
-
-    if( !GetIDFString( aLine, entry, quoted, idx ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 2, missing FIELD 4\n";
-        return false;
-    }
-
-    if( quoted )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 2, invalid FIELD 4 (quoted)\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    // ensure that we have a valid value
-    double val;
-    std::stringstream teststr;
-    teststr << entry;
-
-    if( !( teststr >> val ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 2, invalid FIELD 4 (must be numeric)\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    teststr.clear();
-    teststr << geometry << "_" << partno;
-
-    if( !parent->RegisterOutline( teststr.str() ) )
-        isNewItem = true;
-
-    return true;
-}
-
-
-bool IDF_COMP::parseRec3( const std::string aLine, int& aLoopIndex,
-                          double& aX, double& aY, bool& aClosed )
-{
-    // RECORD 3:
-    // + 0,1 (loop label)
-    // + X coord (float)
-    // + Y coord (float)
-    // + included angle (0 for line, +ang for CCW, -ang for CW, +360 for circle)
-    //
-    // notes:
-    // 1. first entry may not be a circle or arc
-    // 2. it would be nice, but not essential, to ensure that the
-    //      winding is indeed as specified by the loop label
-    //
-
-    double x, y, ang;
-    bool ccw    = false;
-    bool quoted = false;
-    int idx = 0;
-    std::string entry;
-
-    if( !GetIDFString( aLine, entry, quoted, idx ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, no data\n";
-        return false;
-    }
-
-    if( quoted )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, FIELD 1 is quoted\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( entry.compare( "0" ) && entry.compare( "1" ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, FIELD 1 is invalid (must be 0 or 1)\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( !entry.compare( "0" ) )
-        ccw = true;
-
-    if( aLoopIndex == 0 && !ccw )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, LOOP INDEX changed from 0 to 1\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( aLoopIndex == 1 && ccw )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, LOOP INDEX changed from 1 to 0\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( !GetIDFString( aLine, entry, quoted, idx ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, FIELD 2 does not exist\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( quoted )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, FIELD 2 is quoted\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    std::stringstream tstr;
-    tstr.str( entry );
-
-    if( !(tstr >> x ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, invalid X value in FIELD 2\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( !GetIDFString( aLine, entry, quoted, idx ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, FIELD 3 does not exist\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( quoted )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, FIELD 3 is quoted\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    tstr.clear();
-    tstr.str( entry );
-
-    if( !(tstr >> y ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, invalid Y value in FIELD 3\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( !GetIDFString( aLine, entry, quoted, idx ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, FIELD 4 does not exist\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( quoted )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, FIELD 4 is quoted\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    tstr.clear();
-    tstr.str( entry );
-
-    if( !(tstr >> ang ) )
-    {
-        std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, invalid ANGLE value in FIELD 3\n";
-        std::cerr << "    LINE: " << aLine << "\n";
-        return false;
-    }
-
-    if( aLoopIndex == -1 )
-    {
-        // this is the first point; there are some special checks
-        aLoopIndex = ccw ? 0 : 1;
-        aX = x;
-        aY = y;
-        aClosed = false;
-
-        // ensure that the first point is not an arc specification
-        if( ang < -MIN_ANG || ang > MIN_ANG )
-        {
-            std::cerr << "IDF_COMP: *ERROR* invalid RECORD 3, first point has non-zero angle\n";
-            std::cerr << "    LINE: " << aLine << "\n";
-            return false;
-        }
-    }
-    else
-    {
-        // does this close the outline?
-        if( ang < 0.0 ) ang = -ang;
-
-        ang -= 360.0;
-
-        if( ang > -MIN_ANG && ang < MIN_ANG )
-        {
-            // this is  a circle; the loop is closed
-            aClosed = true;
-        }
-        else
-        {
-            x = (aX - x) * (aX - x);
-            y = (aY - y) * (aY - y) + x;
-
-            if( y <= 1e-6 )
-            {
-                // the points are close enough; the loop is closed
-                aClosed = true;
-            }
-        }
-    }
-
-    // NOTE:
-    // 1. ideally we would ensure that there are no arcs with a radius of 0; this entails
-    //    actively calculating the last point as the previous entry could have been an instruction
-    //    to create an arc. This check is sacrificed in the interest of speed.
-    // 2. a bad outline can be crafted by giving at least one valid segment and then introducing
-    //    a circle; such a condition is not checked for here in the interest of speed.
-    // 3. a circle specified with an angle of -360 is invalid, but that condition is not
-    //    tested here.
-
-    return true;
-}
-
-
-// fetch a line from the given input file and trim the ends
-static bool FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment )
-{
-    aLine = "";
-    std::getline( aModel, aLine );
-
-    isComment = false;
-
-    // A comment begins with a '#' and must be the first character on the line
-    if( aLine[0] == '#' )
-        isComment = true;
-
-
-    while( !aLine.empty() && isspace( *aLine.begin() ) )
-        aLine.erase( aLine.begin() );
-
-    while( !aLine.empty() && isspace( *aLine.rbegin() ) )
-        aLine.erase( --aLine.end() );
-
-    if( aLine.empty() )
-        return false;
-
-    return true;
-}
-
-
-// extract an IDF string and move the index to point to the character after the substring
-static bool GetIDFString( const std::string& aLine, std::string& aIDFString,
-                          bool& hasQuotes, int& aIndex )
-{
-    // 1. drop all leading spaces
-    // 2. if the first character is '"', read until the next '"',
-    //    otherwise read until the next space or EOL.
-
-    std::ostringstream ostr;
-
-    int len = aLine.length();
-    int idx = aIndex;
-
-    if( idx < 0 || idx >= len )
-        return false;
-
-    while( isspace( aLine[idx] ) && idx < len ) ++idx;
-
-    if( idx == len )
-    {
-        aIndex = idx;
-        return false;
-    }
-
-    if( aLine[idx] == '"' )
-    {
-        hasQuotes = true;
-        ++idx;
-        while( aLine[idx] != '"' && idx < len )
-            ostr << aLine[idx++];
-
-        if( idx == len )
-        {
-            std::cerr << "GetIDFString(): *ERROR*: unterminated quote mark in line:\n";
-            std::cerr << "LINE: " << aLine << "\n";
-            aIndex = idx;
-            return false;
-        }
-
-        ++idx;
-    }
-    else
-    {
-        hasQuotes = false;
-
-        while( !isspace( aLine[idx] ) && idx < len )
-            ostr << aLine[idx++];
-
-    }
-
-    aIDFString = ostr.str();
-    aIndex = idx;
-
-    return true;
-}
diff --git a/pcbnew/exporters/idf.h b/pcbnew/exporters/idf.h
deleted file mode 100644
index 30c89ddeff..0000000000
--- a/pcbnew/exporters/idf.h
+++ /dev/null
@@ -1,321 +0,0 @@
-/**
- * @file idf.h
- */
-
-/*
- * This program source code file is part of KiCad, a free EDA CAD application.
- *
- * Copyright (C) 2013-2014  Cirilo Bernardo
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, you may find one here:
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- * or you may search the http://www.gnu.org website for the version 2 license,
- * or you may write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
- */
-
-#ifndef IDF_H
-#define IDF_H
-
-#include <wx/string.h>
-#include <set>
-#include <string>
-#include <idf_common.h>
-
-
-/**
- * @Class IDF_COMP
- * is responsible for parsing individual component files and rewriting relevant
- * data to a library file.
- */
-class IDF_COMP
-{
-private:
-    /// filename (full path) of the IDF component footprint
-    wxString componentFile;
-
-    /// reference designator; a valid designator or NOREFDES
-    std::string refdes;
-
-    /// overall translation of the part (component location + 3D offset)
-    double loc_x;
-    double loc_y;
-    double loc_z;
-
-    /// overall rotation of the part (3D Z rotation + component rotation)
-    double rotation;
-
-    /// true if the component is on the top of the board
-    bool top;
-
-    /// geometry of the package; for example, HORIZ, VERT, "HORIZ 0.2 inch"
-    std::string geometry;
-
-    /// package name or part number; for example "TO92" or "BC107"
-    std::string partno;
-
-    /// the owning IDF_LIB instance
-    IDF_LIB* parent;
-
-    /**
-     * Function substituteComponent
-     * places a substitute component footprint into the library file
-     * and creates an appropriate entry for the PLACEMENT section
-     * @param aLibFile is the library file to write to
-     * @return bool: true if data was successfully written
-     */
-    bool substituteComponent( FILE* aLibFile );
-
-    // parse RECORD 2; return TRUE if all is OK, otherwise FALSE
-    bool parseRec2( const std::string aLine, bool& isNewItem );
-
-    // parse RECORD 3; return TRUE if all is OK, otherwise FALSE
-    bool parseRec3( const std::string aLine, int& aLoopIndex,
-                    double& aX, double& aY, bool& aClosed );
-
-public:
-    IDF_COMP( IDF_LIB* aParent );
-
-    /**
-     * Function PlaceComponent
-     * specifies the parameters of an IDF component outline placed on the board
-     * @param aComponentFile is the IDF component file to include
-     * @param aRefDes is the component reference designator; an empty string,
-     *                '~' or '0' all default to "NOREFDES".
-     * @param aLocation is the overall translation of the part (board location + 3D offset)
-     * @param aRotation is the overall rotation of the part (component rotation + 3D Z rotation)
-     * @return bool: true if the specified component file exists
-     */
-    bool PlaceComponent( const wxString aComponentFile, const std::string aRefDes,
-                         double aXLoc, double aYLoc, double aZLoc,
-                         double aRotation, bool isOnTop );
-
-    /**
-     * Function WriteLib
-     * parses the model file to extract information needed by the
-     * PLACEMENT section and writes data (if necessary) to the
-     * library file
-     * @param aLibFile is the library file to write to
-     * @return bool: true if data was successfully written
-     */
-    bool WriteLib( FILE* aLibFile );
-
-    /**
-     * Function WritePlacement
-     * write the .PLACEMENT data of the component to the IDF board @param aLayoutFile
-     * @return bool: true if data was successfully written
-     */
-    bool WritePlacement( FILE* aLayoutFile );
-};
-
-
-/**
- * @Class IDF_LIB
- * stores information on IDF models ( also has an inbuilt NOMODEL model )
- * and is responsible for writing the ELECTRICAL sections of the library file
- * (*.emp) and the PLACEMENT section of the board file.
- */
-class IDF_LIB
-{
-    /// a list of component outline names and a flag to indicate their save state
-    std::set< std::string > regOutlines;
-    std::list< IDF_COMP* > components;
-    bool libWritten;
-
-    /**
-     * Function writeLib
-     * writes all current library information to the output file
-     */
-    bool writeLib( FILE* aLibFile );
-
-    /**
-     * Function writeBrd
-     * write placement information to the board file
-     */
-    bool writeBrd( FILE* aLayoutFile );
-
-public:
-    virtual ~IDF_LIB();
-
-    /**
-     * Function WriteFiles
-     * writes the library entries to the *.emp file (aLibFile) and the
-     * .PLACEMENT section to the *.emn file (aLayoutFile)
-     * @param aLayoutFile IDF board file
-     * @param aLibFile IDF library file
-     * @return bool: true if all data was written successfully
-     */
-    bool WriteFiles( FILE* aLayoutFile, FILE* aLibFile );
-
-    /**
-     * Function RegisterOutline
-     * adds the given string to a list of current outline entities.
-     * @param aGeomPartString is a concatenation of the IDF component's
-     * geometry name and part name; this is used as a unique identifier
-     * to prevent redundant entries in the library output.
-     * @return bool: true if the string was already registered,
-     * false if it is a new registration.
-     */
-    bool RegisterOutline( const std::string aGeomPartString );
-
-    bool PlaceComponent( const wxString  aComponentFile, const std::string aRefDes,
-                         double aXLoc, double aYLoc, double aZLoc,
-                         double aRotation, bool isOnTop );
-};
-
-
-/**
- * @Class IDF_BOARD
- * contains objects necessary for the maintenance of the IDF board and library files.
- */
-class IDF_BOARD
-{
-private:
-    IDF_LIB IDFLib;                         ///< IDF library manager
-    std::list<IDF_DRILL_DATA*> drills;      ///< IDF drill data
-    int outlineIndex;                       ///< next outline index to use
-    bool useThou;                           ///< true if output is THOU
-    double scale;                           ///< scale from KiCad IU to IDF output units
-    double boardThickness;                  ///< total thickness of the PCB
-    bool hasBrdOutlineHdr;                  ///< true when a board outline header has been written
-    int refdesIndex;                        ///< index to generate REFDES for modules which have none
-
-    double offsetX;                         ///< offset to roughly center the board on the world origin
-    double offsetY;
-
-    FILE* layoutFile;                       ///< IDF board file (*.emn)
-    FILE* libFile;                          ///< IDF library file (*.emp)
-
-    /**
-     * Function Write
-     * outputs a .DRILLED_HOLES section compliant with the
-     * IDFv3 specification.
-     * @param aLayoutFile : open file (*.emn) for output
-     */
-    bool WriteDrills( void );
-
-public:
-    IDF_BOARD();
-
-    ~IDF_BOARD();
-
-    // Set up the output files and scale factor;
-    // return TRUE if everything is OK
-    bool Setup( wxString aBoardName, wxString aFullFileName, bool aUseThou, int aBoardThickness );
-
-    // Finish a board
-    // Write out all current data and close files.
-    // Return true for success
-    bool Finish( void );
-
-    /**
-     * Function GetScale
-     * returns the output scaling factor
-     */
-    double GetScale( void );
-
-    /**
-     * Function SetOffset
-     * sets the global coordinate offsets
-     */
-    void SetOffset( double x, double y );
-
-    /**
-     * Function GetOffset
-     * returns the global coordinate offsets
-     */
-    void GetOffset( double& x, double& y );
-
-    // Add an outline; the very first outline is the board perimeter;
-    // all additional outlines are cutouts.
-    bool AddOutline( IDF_OUTLINE& aOutline );
-
-    /**
-     * Function AddDrill
-     * creates a drill entry and adds it to the list of PCB holes
-     * @param dia : drill diameter
-     * @param x : X coordinate of the drill center
-     * @param y : Y coordinate of the drill center
-     * @param plating : flag, PTH or NPTH
-     * @param refdes : component Reference Designator
-     * @param holetype : purpose of hole
-     * @param owner : one of MCAD, ECAD, UNOWNED
-     */
-    bool AddDrill( double dia, double x, double y,
-            IDF3::KEY_PLATING plating,
-            const std::string refdes,
-            const std::string holeType,
-            IDF3::KEY_OWNER owner );
-
-    /**
-     * Function AddSlot
-     * creates a slot cutout within the IDF BOARD section; this is a deficient representation
-     * of a KiCad 'oval' drill; IDF is unable to represent a plated slot and unable to
-     * represent the Reference Designator association with a slot.
-     */
-    bool AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY );
-
-    bool PlaceComponent( const wxString aComponentFile, const std::string aRefDes,
-                         double aXLoc, double aYLoc, double aZLoc,
-                         double aRotation, bool isOnTop );
-
-    std::string GetRefDes( void );
-};
-
-
-/**
- * @Class IDF_DRILL_DATA
- * contains information describing a drilled hole and is responsible for
- * writing this information to a file in compliance with the IDFv3 specification.
- */
-class IDF_DRILL_DATA
-{
-private:
-    double dia;
-    double x;
-    double y;
-    IDF3::KEY_PLATING plating;
-    IDF3::KEY_REFDES kref;
-    IDF3::KEY_HOLETYPE khole;
-    std::string refdes;
-    std::string holetype;
-    IDF3::KEY_OWNER owner;
-
-public:
-    /**
-     * Constructor IDF_DRILL_DATA
-     * creates a drill entry with information compliant with the
-     * IDFv3 specifications.
-     * @param aDrillDia : drill diameter
-     * @param aPosX : X coordinate of the drill center
-     * @param aPosY : Y coordinate of the drill center
-     * @param aPlating : flag, PTH or NPTH
-     * @param aRefDes : component Reference Designator
-     * @param aHoleType : purpose of hole
-     * @param aOwner : one of MCAD, ECAD, UNOWNED
-     */
-    IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
-            IDF3::KEY_PLATING aPlating,
-            const std::string aRefDes,
-            const std::string aHoleType,
-            IDF3::KEY_OWNER aOwner );
-
-    /**
-     * Function Write
-     * writes a single line representing the hole within a .DRILLED_HOLES section
-     */
-    bool Write( FILE* aLayoutFile );
-};
-
-#endif  // IDF_H
diff --git a/pcbnew/exporters/idf_common.cpp b/pcbnew/exporters/idf_common.cpp
deleted file mode 100644
index 47cfcde294..0000000000
--- a/pcbnew/exporters/idf_common.cpp
+++ /dev/null
@@ -1,484 +0,0 @@
-/**
- * file: idf_common.cpp
- *
- * This program source code file is part of KiCad, a free EDA CAD application.
- *
- * Copyright (C) 2013-2014  Cirilo Bernardo
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, you may find one here:
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- * or you may search the http://www.gnu.org website for the version 2 license,
- * or you may write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
- */
-
-#include <list>
-#include <string>
-#include <iostream>
-#include <cstdio>
-#include <cmath>
-#include <richio.h>
-#include <idf_common.h>
-#include <build_version.h>
-
-#ifdef DEBUG_IDF
-void IDF3::PrintSeg( IDF_SEGMENT* aSegment )
-{
-    if( aSegment->IsCircle() )
-    {
-        fprintf(stdout, "printSeg(): CIRCLE: C(%.3f, %.3f) P(%.3f, %.3f) rad. %.3f\n",
-                aSegment->startPoint.x, aSegment->startPoint.y,
-                aSegment->endPoint.x, aSegment->endPoint.y,
-                aSegment->radius );
-        return;
-    }
-
-    if( aSegment->angle < -MIN_ANG || aSegment->angle > MIN_ANG )
-    {
-        fprintf(stdout, "printSeg(): ARC: p1(%.3f, %.3f) p2(%.3f, %.3f) ang. %.3f\n",
-                aSegment->startPoint.x, aSegment->startPoint.y,
-                aSegment->endPoint.x, aSegment->endPoint.y,
-                aSegment->angle );
-        return;
-    }
-
-    fprintf(stdout, "printSeg(): LINE: p1(%.3f, %.3f) p2(%.3f, %.3f)\n",
-            aSegment->startPoint.x, aSegment->startPoint.y,
-            aSegment->endPoint.x, aSegment->endPoint.y );
-
-    return;
-}
-#endif
-
-
-bool IDF_POINT::Matches( const IDF_POINT& aPoint, double aRadius )
-{
-    double dx = x - aPoint.x;
-    double dy = y - aPoint.y;
-
-    double d2 = dx * dx + dy * dy;
-
-    if( d2 <= aRadius * aRadius )
-        return true;
-
-    return false;
-}
-
-
-double IDF_POINT::CalcDistance( const IDF_POINT& aPoint ) const
-{
-    double dx   = aPoint.x - x;
-    double dy   = aPoint.y - y;
-    double dist = sqrt( dx * dx + dy * dy );
-
-    return dist;
-}
-
-
-double IDF3::CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
-{
-    return atan2( aEndPoint.y - aStartPoint.y, aEndPoint.x - aStartPoint.x );
-}
-
-
-double IDF3::CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
-{
-    double ang = CalcAngleRad( aStartPoint, aEndPoint );
-
-    // round to thousandths of a degree
-    int iang = int (ang / M_PI * 1800000.0);
-
-    ang = iang / 10000.0;
-
-    return ang;
-}
-
-
-void IDF3::GetOutline( std::list<IDF_SEGMENT*>& aLines,
-                       IDF_OUTLINE& aOutline )
-{
-    aOutline.Clear();
-
-    // NOTE: To tell if the point order is CCW or CW,
-    // sum all:  (endPoint.X[n] - startPoint.X[n])*(endPoint[n] + startPoint.Y[n])
-    // If the result is >0, the direction is CW, otherwise
-    // it is CCW. Note that the result cannot be 0 unless
-    // we have a bounded area of 0.
-
-    // First we find the segment with the leftmost point
-    std::list<IDF_SEGMENT*>::iterator bl    = aLines.begin();
-    std::list<IDF_SEGMENT*>::iterator el    = aLines.end();
-    std::list<IDF_SEGMENT*>::iterator idx   = bl++;       // iterator for the object with minX
-
-    double minx = (*idx)->GetMinX();
-    double curx;
-
-    while( bl != el )
-    {
-        curx = (*bl)->GetMinX();
-
-        if( curx < minx )
-        {
-            minx = curx;
-            idx = bl;
-        }
-
-        ++bl;
-    }
-
-    aOutline.push( *idx );
-#ifdef DEBUG_IDF
-    PrintSeg( *idx );
-#endif
-    aLines.erase( idx );
-
-    // If the item is a circle then we're done
-    if( aOutline.front()->IsCircle() )
-        return;
-
-    // Assemble the loop
-    bool complete = false;  // set if loop is complete
-    bool matched;           // set if a segment's end point was matched
-
-    while( !complete )
-    {
-        matched = false;
-        bl  = aLines.begin();
-        el  = aLines.end();
-
-        while( bl != el && !matched )
-        {
-            if( (*bl)->MatchesStart( aOutline.back()->endPoint ) )
-            {
-                if( (*bl)->IsCircle() )
-                {
-                    // a circle on the perimeter is pathological but we just ignore it
-                    ++bl;
-                }
-                else
-                {
-                    matched = true;
-#ifdef DEBUG_IDF
-                    PrintSeg( *bl );
-#endif
-                    aOutline.push( *bl );
-                    aLines.erase( bl );
-                }
-
-                continue;
-            }
-
-            ++bl;
-        }
-
-        if( !matched )
-        {
-            // attempt to match the end points
-            bl  = aLines.begin();
-            el  = aLines.end();
-
-            while( bl != el && !matched )
-            {
-                if( (*bl)->MatchesEnd( aOutline.back()->endPoint ) )
-                {
-                    if( (*bl)->IsCircle() )
-                    {
-                        // a circle on the perimeter is pathological but we just ignore it
-                        ++bl;
-                    }
-                    else
-                    {
-                        matched = true;
-                        (*bl)->SwapEnds();
-#ifdef DEBUG_IDF
-                        printSeg( *bl );
-#endif
-                        aOutline.push( *bl );
-                        aLines.erase( bl );
-                    }
-
-                    continue;
-                }
-
-                ++bl;
-            }
-        }
-
-        if( !matched )
-        {
-            // still no match - attempt to close the loop
-            if( (aOutline.size() > 1) || ( aOutline.front()->angle < -MIN_ANG )
-                || ( aOutline.front()->angle > MIN_ANG ) )
-            {
-                // close the loop
-                IDF_SEGMENT* seg = new IDF_SEGMENT( aOutline.back()->endPoint,
-                                                    aOutline.front()->startPoint );
-
-                if( seg )
-                {
-                    complete = true;
-#ifdef DEBUG_IDF
-                    printSeg( seg );
-#endif
-                    aOutline.push( seg );
-                    break;
-                }
-            }
-
-            // the outline is bad; drop the segments
-            aOutline.Clear();
-
-            return;
-        }
-
-        // check if the loop is complete
-        if( aOutline.front()->MatchesStart( aOutline.back()->endPoint ) )
-        {
-            complete = true;
-            break;
-        }
-    }
-}
-
-
-IDF_SEGMENT::IDF_SEGMENT()
-{
-    angle = 0.0;
-    offsetAngle = 0.0;
-    radius = 0.0;
-}
-
-
-IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
-{
-    angle = 0.0;
-    offsetAngle = 0.0;
-    radius = 0.0;
-    startPoint = aStartPoint;
-    endPoint = aEndPoint;
-}
-
-
-IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint,
-                          const IDF_POINT& aEndPoint,
-                          double aAngle,
-                          bool aFromKicad )
-{
-    double diff = abs( aAngle ) - 360.0;
-
-    if( ( diff < MIN_ANG
-        && diff > -MIN_ANG ) || ( aAngle < MIN_ANG && aAngle > -MIN_ANG ) || (!aFromKicad) )
-    {
-        angle = 0.0;
-        startPoint = aStartPoint;
-        endPoint = aEndPoint;
-
-        if( diff < MIN_ANG && diff > -MIN_ANG )
-        {
-            angle = 360.0;
-            center = aStartPoint;
-            offsetAngle = 0.0;
-            radius = aStartPoint.CalcDistance( aEndPoint );
-        }
-        else if( aAngle < MIN_ANG && aAngle > -MIN_ANG )
-        {
-            CalcCenterAndRadius();
-        }
-
-        return;
-    }
-
-    // we need to convert from the KiCad arc convention
-    angle = aAngle;
-
-    center = aStartPoint;
-
-    offsetAngle = IDF3::CalcAngleDeg( aStartPoint, aEndPoint );
-
-    radius = aStartPoint.CalcDistance( aEndPoint );
-
-    startPoint = aEndPoint;
-
-    double ang = offsetAngle + aAngle;
-    ang = (ang / 180.0) * M_PI;
-
-    endPoint.x  = ( radius * cos( ang ) ) + center.x;
-    endPoint.y  = ( radius * sin( ang ) ) + center.y;
-}
-
-
-bool IDF_SEGMENT::MatchesStart( const IDF_POINT& aPoint, double aRadius )
-{
-    return startPoint.Matches( aPoint, aRadius );
-}
-
-
-bool IDF_SEGMENT::MatchesEnd( const IDF_POINT& aPoint, double aRadius )
-{
-    return endPoint.Matches( aPoint, aRadius );
-}
-
-
-void IDF_SEGMENT::CalcCenterAndRadius( void )
-{
-    // NOTE:  this routine does not check if the points are the same
-    // or too close to be sensible in a production setting.
-
-    double offAng = IDF3::CalcAngleRad( startPoint, endPoint );
-    double d = startPoint.CalcDistance( endPoint ) / 2.0;
-    double xm   = ( startPoint.x + endPoint.x ) * 0.5;
-    double ym   = ( startPoint.y + endPoint.y ) * 0.5;
-
-    radius = d / sin( angle * M_PI / 180.0 );
-
-    if( radius < 0.0 )
-    {
-        radius = -radius;
-    }
-
-    // calculate the height of the triangle with base d and hypotenuse r
-    double dh2 = radius * radius - d * d;
-
-    if( dh2 < 0 )
-    {
-        // this should only ever happen due to rounding errors when r == d
-        dh2 = 0;
-    }
-
-    double h = sqrt( dh2 );
-
-    if( angle > 0.0 )
-        offAng += M_PI2;
-    else
-        offAng -= M_PI2;
-
-    if( ( angle > M_PI ) || ( angle < -M_PI ) )
-        offAng += M_PI;
-
-    center.x = h * cos( offAng ) + xm;
-    center.y = h * sin( offAng ) + ym;
-
-    offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
-}
-
-
-bool IDF_SEGMENT::IsCircle( void )
-{
-    double diff = abs( angle ) - 360.0;
-
-    if( ( diff < MIN_ANG ) && ( diff > -MIN_ANG ) )
-        return true;
-
-    return false;
-}
-
-
-double IDF_SEGMENT::GetMinX( void )
-{
-    if( angle == 0.0 )
-        return std::min( startPoint.x, endPoint.x );
-
-    // Calculate the leftmost point of the circle or arc
-
-    if( IsCircle() )
-    {
-        // if only everything were this easy
-        return center.x - radius;
-    }
-
-    // cases:
-    // 1. CCW arc: if offset + included angle >= 180 deg then
-    // MinX = center.x - radius, otherwise MinX is the
-    // same as for the case of a line.
-    // 2. CW arc: if offset + included angle <= -180 deg then
-    // MinX = center.x - radius, otherwise MinX is the
-    // same as for the case of a line.
-
-    if( angle > 0 )
-    {
-        // CCW case
-        if( ( offsetAngle + angle ) >= 180.0 )
-        {
-            return center.x - radius;
-        }
-        else
-        {
-            return std::min( startPoint.x, endPoint.x );
-        }
-    }
-
-    // CW case
-    if( ( offsetAngle + angle ) <= -180.0 )
-    {
-        return center.x - radius;
-    }
-
-    return std::min( startPoint.x, endPoint.x );
-}
-
-
-void IDF_SEGMENT::SwapEnds( void )
-{
-    if( IsCircle() )
-    {
-        // reverse the direction
-        angle = -angle;
-        return;
-    }
-
-    IDF_POINT tmp = startPoint;
-    startPoint = endPoint;
-    endPoint = tmp;
-
-    if( ( angle < MIN_ANG ) && ( angle > -MIN_ANG ) )
-        return;         // nothing more to do
-
-        // change the direction of the arc
-        angle = -angle;
-    // calculate the new offset angle
-    offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
-}
-
-
-void IDF_OUTLINE::push( IDF_SEGMENT* item )
-{
-    if( !outline.empty() )
-    {
-        if( item->IsCircle() )
-        {
-            // not allowed
-            wxString msg = wxT( "INVALID GEOMETRY: a circle is being added to a non-empty outline" );
-            THROW_IO_ERROR( msg );
-        }
-        else
-        {
-            if( outline.back()->IsCircle() )
-            {
-                // we can't add lines to a circle
-                wxString msg = wxT( "INVALID GEOMETRY: a line is being added to a circular outline" );
-                THROW_IO_ERROR( msg );
-            }
-            else if( !item->MatchesStart( outline.back()->endPoint ) )
-            {
-                // startPoint[N] != endPoint[N -1]
-                wxString msg = wxT( "INVALID GEOMETRY: disjoint segments" );
-                THROW_IO_ERROR( msg );
-            }
-        }
-    }
-
-    outline.push_back( item );
-    dir += ( outline.back()->endPoint.x - outline.back()->startPoint.x )
-    * ( outline.back()->endPoint.y + outline.back()->startPoint.y );
-}
diff --git a/pcbnew/exporters/idf_common.h b/pcbnew/exporters/idf_common.h
deleted file mode 100644
index ff432de827..0000000000
--- a/pcbnew/exporters/idf_common.h
+++ /dev/null
@@ -1,290 +0,0 @@
-/**
- * @file idf_common.h
- */
-
-/*
- * This program source code file is part of KiCad, a free EDA CAD application.
- *
- * Copyright (C) 2013-2014  Cirilo Bernardo
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, you may find one here:
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- * or you may search the http://www.gnu.org website for the version 2 license,
- * or you may write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
- */
-
-#ifndef IDF_COMMON_H
-#define IDF_COMMON_H
-
-#include <list>
-
-#ifndef M_PI
-#define M_PI 3.1415926535897932384626433832795028841
-#endif
-
-#ifndef M_PI2
-#define M_PI2 ( M_PI / 2.0 )
-#endif
-
-#ifndef M_PI4
-#define M_PI4 ( M_PI / 4.0 )
-#endif
-
-// differences in angle smaller than MIN_ANG are considered equal
-#define MIN_ANG     (0.01)
-
-class IDF_POINT;
-class IDF_SEGMENT;
-class IDF_DRILL_DATA;
-class IDF_OUTLINE;
-class IDF_LIB;
-
-namespace IDF3 {
-    enum KEY_OWNER
-    {
-        UNOWNED = 0,        // < either MCAD or ECAD may modify a feature
-        MCAD,               // < only MCAD may modify a feature
-        ECAD                // < only ECAD may modify a feature
-    };
-
-    enum KEY_HOLETYPE
-    {
-        PIN = 0,            // < drill hole is for a pin
-        VIA,                // < drill hole is for a via
-        MTG,                // < drill hole is for mounting
-        TOOL,               // < drill hole is for tooling
-        OTHER               // < user has specified a custom type
-    };
-
-    enum KEY_PLATING
-    {
-        PTH = 0,            // < Plate-Through Hole
-        NPTH                // < Non-Plate-Through Hole
-    };
-
-    enum KEY_REFDES
-    {
-        BOARD = 0,          // < feature is associated with the board
-        NOREFDES,           // < feature is associated with a component with no RefDes
-        PANEL,              // < feature is associated with an IDF panel
-        REFDES              // < reference designator as assigned by the CAD software
-    };
-
-    // calculate the angle between the horizon and the segment aStartPoint to aEndPoint
-    double  CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
-    double  CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
-
-    // take contiguous elements from 'lines' and stuff them into 'outline'
-    void GetOutline( std::list<IDF_SEGMENT*>& aLines,
-                     IDF_OUTLINE& aOutline );
-
-#ifdef DEBUG_IDF
-    // prints out segment information for debug purposes
-    void PrintSeg( IDF_SEGMENT* aSegment );
-#endif
-}
-
-
-/**
- * @Class IDF_POINT
- * represents a point
- */
-class IDF_POINT
-{
-public:
-    double x;   // < X coordinate
-    double y;   // < Y coordinate
-
-    IDF_POINT()
-    {
-        x = 0.0;
-        y = 0.0;
-    }
-
-    /**
-     * Function Matches()
-     * returns true if the given coordinate point is within the given radius
-     * of the point.
-     * @param aPoint : coordinates of the point being compared
-     * @param aRadius : radius within which the points are considered the same
-     */
-    bool    Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 );
-    double  CalcDistance( const IDF_POINT& aPoint ) const;
-};
-
-
-/**
- * @Class IDF_SEGMENT
- * represents a geometry segment as used in IDFv3 outlines
- */
-class IDF_SEGMENT
-{
-private:
-    /**
-     * Function CalcCenterAndRadius()
-     * Calculates the center, radius, and angle between center and start point given the
-     * IDF compliant points and included angle.
-     * @var startPoint, @var endPoint, and @var angle must be set prior as per IDFv3
-     */
-    void CalcCenterAndRadius( void );
-
-public:
-    IDF_POINT startPoint;   // starting point in IDF coordinates
-    IDF_POINT endPoint;     // end point in IDF coordinates
-    IDF_POINT   center;     // center of an arc or circle; used primarily for calculating min X
-    double  angle;          // included angle (degrees) according to IDFv3 specification
-    double  offsetAngle;    // angle between center and start of arc; used to speed up some calcs.
-    double  radius;         // radius of the arc or circle; used to speed up some calcs.
-
-    /**
-     * Function IDF_SEGMENT()
-     * initializes the internal variables
-     */
-    IDF_SEGMENT();
-
-    /**
-     * Function IDF_SEGMENT( start, end )
-     * creates a straight segment
-     */
-    IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
-
-    /**
-     * Function IDF_SEGMENT( start, end )
-     * creates a straight segment, arc, or circle depending on the angle
-     * @param aStartPoint : start point (center if using KiCad convention, otherwise IDF convention)
-     * @param aEndPoint : end point (start of arc if using KiCad convention, otherwise IDF convention)
-     * @param aAngle : included angle; the KiCad convention is equivalent to the IDF convention
-     * @param fromKicad : set true if we need to convert from KiCad to IDF convention
-     */
-    IDF_SEGMENT( const IDF_POINT& aStartPoint,
-                 const IDF_POINT& aEndPoint,
-                 double aAngle,
-                 bool aFromKicad );
-
-    /**
-     * Function MatchesStart()
-     * returns true if the given coordinate is within a radius 'rad'
-     * of the start point.
-     * @param aPoint : coordinates of the point being compared
-     * @param aRadius : radius within which the points are considered the same
-     */
-    bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 );
-
-    /**
-     * Function MatchesEnd()
-     * returns true if the given coordinate is within a radius 'rad'
-     * of the end point.
-     * @param aPoint : coordinates of the point being compared
-     * @param aRadius : radius within which the points are considered the same
-     */
-    bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 );
-
-    /**
-     * Function IsCircle()
-     * returns true if this segment is a circle
-     */
-    bool IsCircle( void );
-
-    /**
-     * Function GetMinX()
-     * returns the minimum X coordinate of this segment
-     */
-    double GetMinX( void );
-
-    /**
-     * Function SwapEnds()
-     * Swaps the start and end points and alters internal
-     * variables as necessary for arcs
-     */
-    void SwapEnds( void );
-};
-
-
-/**
- * @Class IDF_OUTLINE
- * contains segment and winding information for an IDF outline
- */
-class IDF_OUTLINE
-{
-private:
-    double dir;
-    std::list<IDF_SEGMENT*> outline;
-
-public:
-    IDF_OUTLINE() { dir = 0.0; }
-    ~IDF_OUTLINE() { Clear(); }
-
-    // returns true if the current list of points represents a counterclockwise winding
-    bool IsCCW( void )
-    {
-        if( dir > 0.0 )
-            return false;
-
-        return true;
-    }
-
-    // clears the internal list of outline segments
-    void Clear( void )
-    {
-        dir = 0.0;
-
-        while( !outline.empty() )
-        {
-            delete outline.front();
-            outline.pop_front();
-        }
-    }
-
-    // returns the size of the internal segment list
-    size_t size( void )
-    {
-        return outline.size();
-    }
-
-    // returns true if the internal segment list is empty
-    bool empty( void )
-    {
-        return outline.empty();
-    }
-
-    // return the front() of the internal segment list
-    IDF_SEGMENT*& front( void )
-    {
-        return outline.front();
-    }
-
-    // return the back() of the internal segment list
-    IDF_SEGMENT*& back( void )
-    {
-        return outline.back();
-    }
-
-    // return the begin() iterator of the internal segment list
-    std::list<IDF_SEGMENT*>::iterator begin( void )
-    {
-        return outline.begin();
-    }
-
-    // return the end() iterator of the internal segment list
-    std::list<IDF_SEGMENT*>::iterator end( void )
-    {
-        return outline.end();
-    }
-
-    // push a segment onto the internal list
-    void push( IDF_SEGMENT* item );
-};
-
-#endif  // IDF_COMMON_H
diff --git a/utils/idftools/idf_examples/test_donut.emn b/utils/idftools/idf_examples/test_donut.emn
new file mode 100644
index 0000000000..fd5b87f7cc
--- /dev/null
+++ b/utils/idftools/idf_examples/test_donut.emn
@@ -0,0 +1,45 @@
+.HEADER
+BOARD_FILE 3.0 "Created by some software" 2014/02/01.15:09:15 1
+"test_donut" MM
+.END_HEADER
+
+# The board outline is a simple square with a small hole in it
+.BOARD_OUTLINE ECAD
+1.60000
+0 -100 100 0
+0 -100 -100 0
+0 100 -100 0
+0 100 100 0
+0 -100 100 0
+1 0 0 0
+1 5 0 360
+.END_BOARD_OUTLINE
+
+# This OTHER OUTLINE is a square toroid
+.OTHER_OUTLINE UNOWNED
+MY_DONUT 30 TOP
+0 0 0 0
+0 75 0 360
+1 0 0 0
+1 30 0 360
+.END_OTHER_OUTLINE
+
+# This OTHER OUTLINE is a square with a hole
+.OTHER_OUTLINE UNOWNED
+MY_NOT_DONUT 2 BOTTOM
+0 -50 50 0
+0 -50 -50 0
+0 50 -50 0
+0 50 50 0
+0 -50 50 0
+1 0 0 0
+1 10 0 360
+2 0 50 0
+2 0 75 360
+3 50 0 0
+3 75 0 360
+4 0 -50 0
+4 0 -75 360
+5 -50 0 0
+5 -75 0 360
+.END_OTHER_OUTLINE
diff --git a/utils/idftools/idf_examples/test_donut.emp b/utils/idftools/idf_examples/test_donut.emp
new file mode 100644
index 0000000000..d3c09b7e7b
--- /dev/null
+++ b/utils/idftools/idf_examples/test_donut.emp
@@ -0,0 +1,5 @@
+.HEADER
+LIBRARY_FILE 3.0 "Created by some software" 2014/02/01.15:09:15 1
+.END_HEADER
+
+# This file contains no component outlines
\ No newline at end of file
diff --git a/utils/idftools/idf_outlines.cpp b/utils/idftools/idf_outlines.cpp
index 9558d7dab2..43d1a4cbb2 100644
--- a/utils/idftools/idf_outlines.cpp
+++ b/utils/idftools/idf_outlines.cpp
@@ -693,6 +693,10 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
     std::list<IDF_SEGMENT*>::iterator bo;
     std::list<IDF_SEGMENT*>::iterator eo;
 
+    if( !aOutline )
+        throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+                          "\n* BUG: NULL outline pointer" ) );
+
     if( aOutline->size() == 1 )
     {
         if( !aOutline->front()->IsCircle() )
diff --git a/utils/idftools/idf_parser.cpp b/utils/idftools/idf_parser.cpp
index 018be43aa2..a1ac7da92c 100644
--- a/utils/idftools/idf_parser.cpp
+++ b/utils/idftools/idf_parser.cpp
@@ -36,6 +36,48 @@
 using namespace std;
 using namespace IDF3;
 
+
+static bool MatchCompOutline( IDF3_COMP_OUTLINE* aOutlineA, IDF3_COMP_OUTLINE* aOutlineB )
+{
+    if( aOutlineA->GetComponentClass() != aOutlineB->GetComponentClass() )
+        return false;
+
+    if( aOutlineA->OutlinesSize() != aOutlineB->OutlinesSize() )
+        return false;
+
+    // are both outlines empty?
+    if( aOutlineA->OutlinesSize() == 0 )
+        return true;
+
+    IDF_OUTLINE* opA = aOutlineA->GetOutline( 0 );
+    IDF_OUTLINE* opB = aOutlineB->GetOutline( 0 );
+
+    if( opA->size() != opB->size() )
+        return false;
+
+    if( opA->size() == 0 )
+        return true;
+
+    std::list<IDF_SEGMENT*>::iterator olAs = opA->begin();
+    std::list<IDF_SEGMENT*>::iterator olAe = opA->end();
+    std::list<IDF_SEGMENT*>::iterator olBs = opB->begin();
+
+    while( olAs != olAe )
+    {
+        if( !(*olAs)->MatchesStart( (*olBs)->startPoint ) )
+            return false;
+
+        if( !(*olAs)->MatchesEnd( (*olBs)->endPoint ) )
+            return false;
+
+        ++olAs;
+        ++olBs;
+    }
+
+    return true;
+}
+
+
 /*
  * CLASS: IDF3_COMP_OUTLINE_DATA
  * This represents the outline placement
@@ -285,7 +327,7 @@ bool IDF3_COMP_OUTLINE_DATA::readPlaceData( std::ifstream &aBoardFile,
         // component is given a unique RefDes. This class of defect
         // is one reason IDF does not work well in faithfully
         // conveying information between ECAD and MCAD.
-        refdes = token;
+        refdes = aBoard->GetNewRefDes();
     }
     else if( CompareToken( "BOARD", token ) )
     {
@@ -1297,6 +1339,7 @@ IDF3_BOARD::IDF3_BOARD( IDF3::CAD_TYPE aCadType )
     userYoff       = 0.0;
     brdFileVersion = 0;
     libFileVersion = 0;
+    iRefDes        = 0;
 
     // unlike other outlines which are created as necessary,
     // the board outline always exists and its parent must
@@ -1314,6 +1357,18 @@ IDF3_BOARD::~IDF3_BOARD()
     return;
 }
 
+
+const std::string& IDF3_BOARD::GetNewRefDes( void )
+{
+    ostringstream ostr;
+    ostr << "NOREFDESn" << iRefDes++;
+
+    sRefDes = ostr.str();
+
+    return sRefDes;
+}
+
+
 #ifndef DISABLE_IDF_OWNERSHIP
 bool IDF3_BOARD::checkComponentOwnership( int aSourceLine, const char* aSourceFunc,
                                           IDF3_COMPONENT* aComponent )
@@ -2422,7 +2477,12 @@ void IDF3_BOARD::readLibSection( std::ifstream& aLibFile, IDF3::FILE_STATE& aLib
             }
             else
             {
-                delete pout;
+                if( MatchCompOutline( pout, cop ) )
+                {
+                    delete pout;
+                    // everything is fine; the outlines are genuine duplicates
+                    return;
+                }
 
                 ostringstream ostr;
                 ostr << "invalid IDF library\n";
@@ -3131,7 +3191,7 @@ bool  IDF3_BOARD::SetBoardVersion( int aVersion )
     {
         ostringstream ostr;
         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
-        ostr << "*  board version (" << aVersion << ") must be > 0";
+        ostr << "*  board version (" << aVersion << ") must be >= 0";
         errormsg = ostr.str();
 
         return false;
@@ -3155,7 +3215,7 @@ bool  IDF3_BOARD::SetLibraryVersion( int aVersion )
     {
         ostringstream ostr;
         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
-        ostr << "* library version (" << aVersion << ") must be > 0";
+        ostr << "* library version (" << aVersion << ") must be >= 0";
         errormsg = ostr.str();
 
         return false;
@@ -3741,39 +3801,13 @@ IDF3_COMPONENT* IDF3_BOARD::FindComponent( std::string aRefDes )
 
 // returns a pointer to a component outline object or NULL
 // if the object doesn't exist
-IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const std::string aGeomName,
-                                                    const std::string aPartName,
-                                                    wxString aFullFileName )
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( wxString aFullFileName )
 {
-    std::ostringstream ostr;
-    ostr << aGeomName << "_" << aPartName;
-
-    IDF3_COMP_OUTLINE* cp = GetComponentOutline( ostr.str() );
-
-    if( cp != NULL )
-        return cp;
-
     std::string fname = TO_UTF8( aFullFileName );
-
-    cp = new IDF3_COMP_OUTLINE( this );
-
-    if( cp == NULL )
-    {
-        ostringstream ostr;
-        ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
-        cerr << "* failed to create outline with UID '" << aGeomName << "_";
-        cerr << aPartName << "'\n";
-        cerr << "* filename: '" << fname << "'";
-        errormsg = ostr.str();
-
-        return NULL;
-    }
-
     wxFileName idflib( aFullFileName );
 
     if( !idflib.IsOk() )
     {
-        delete cp;
         ostringstream ostr;
         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
         cerr << "* invalid file name: '" << fname << "'";
@@ -3784,7 +3818,6 @@ IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const std::string aGeomName,
 
     if( !idflib.FileExists() )
     {
-        delete cp;
         ostringstream ostr;
         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
         cerr << "* no such file: '" << fname  << "'";
@@ -3795,7 +3828,6 @@ IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const std::string aGeomName,
 
     if( !idflib.IsFileReadable() )
     {
-        delete cp;
         ostringstream ostr;
         ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
         cerr << "* cannot read file: '" << fname << "'";
@@ -3804,6 +3836,24 @@ IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const std::string aGeomName,
         return NULL;
     }
 
+    std::map< std::string, std::string >::iterator itm = uidFileList.find( fname );
+
+    if( itm != uidFileList.end() )
+        return GetComponentOutline( itm->second );
+
+    IDF3_COMP_OUTLINE* cp = new IDF3_COMP_OUTLINE( this );
+
+    if( cp == NULL )
+    {
+        ostringstream ostr;
+        ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+        cerr << "* failed to create outline\n";
+        cerr << "* filename: '" << fname << "'";
+        errormsg = ostr.str();
+
+        return NULL;
+    }
+
     std::ifstream model;
     model.exceptions ( std::ifstream::badbit );
 
@@ -3867,7 +3917,92 @@ IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const std::string aGeomName,
 
     model.close();
 
-    return cp;
+    // check the unique ID against the list from library components
+    std::list< std::string >::iterator lsts = uidLibList.begin();
+    std::list< std::string >::iterator lste = uidLibList.end();
+    std::string uid = cp->GetUID();
+    IDF3_COMP_OUTLINE* oldp = NULL;
+
+    while( lsts != lste )
+    {
+        if( ! lsts->compare( uid ) )
+        {
+            oldp = GetComponentOutline( uid );
+
+            if( MatchCompOutline( cp, oldp ) )
+            {
+                // everything is fine; the outlines are genuine duplicates; delete the copy
+                delete cp;
+                // make sure we can find the item via its filename
+                uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+                // return the pointer to the original
+                return oldp;
+            }
+            else
+            {
+                delete cp;
+                ostringstream ostr;
+                ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+                ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
+                ostr << "* original loaded from library, duplicate in current file\n";
+                ostr << "* file: '" << fname << "'";
+
+                errormsg = ostr.str();
+                return NULL;
+            }
+        }
+
+        ++lsts;
+    }
+
+    // if we got this far then any duplicates are from files previously read
+    oldp = GetComponentOutline( uid );
+
+    if( oldp == NULL )
+    {
+        // everything is fine, there are no existing entries
+        uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+        compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>( uid, cp ) );
+
+        return cp;
+    }
+
+    if( MatchCompOutline( cp, oldp ) )
+    {
+        // everything is fine; the outlines are genuine duplicates; delete the copy
+        delete cp;
+        // make sure we can find the item via its other filename
+        uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+        // return the pointer to the original
+        return oldp;
+    }
+
+    delete cp;
+
+    // determine the file name of the first instance
+    std::map< std::string, std::string >::iterator ufls = uidFileList.begin();
+    std::map< std::string, std::string >::iterator ufle = uidFileList.end();
+    std::string oldfname;
+
+    while( ufls != ufle )
+    {
+        if( ! ufls->second.compare( uid ) )
+        {
+            oldfname = ufls->first;
+            break;
+        }
+
+        ++ufls;
+    }
+
+    ostringstream ostr;
+    ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+    ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
+    ostr << "* original file: '" << oldfname << "'\n";
+    ostr << "* this file: '" << fname << "'";
+
+    errormsg = ostr.str();
+    return NULL;
 }
 
 
@@ -3940,8 +4075,12 @@ void IDF3_BOARD::Clear( void )
     libSource.clear();
     brdDate.clear();
     libDate.clear();
+    uidFileList.clear();
+    uidLibList.clear();
     brdFileVersion = 0;
     libFileVersion = 0;
+    iRefDes = 0;
+    sRefDes.clear();
 
     // delete comment lists
     noteComments.clear();
diff --git a/utils/idftools/idf_parser.h b/utils/idftools/idf_parser.h
index cc7909f347..e666e17c7f 100644
--- a/utils/idftools/idf_parser.h
+++ b/utils/idftools/idf_parser.h
@@ -463,6 +463,8 @@ public:
 class IDF3_BOARD
 {
 private:
+    std::map< std::string, std::string > uidFileList;           // map of files opened and UIDs
+    std::list< std::string > uidLibList;                        // list of UIDs read from a library file
     std::string errormsg;                                       // string for passing error messages to user
     std::list< IDF_NOTE* >     notes;                           // IDF notes
     std::list< std::string >   noteComments;                    // comment list for NOTES section
@@ -476,6 +478,8 @@ private:
     IDF3::CAD_TYPE   cadType;
     IDF3::IDF_UNIT   unit;
     IDF3::IDF_VERSION   idfVer;                                 // IDF version of Board or Library
+    int iRefDes;                                                // counter for automatically numbered NOREFDES items
+    std::string sRefDes;
 
     std::string idfSource;  // SOURCE string to use when writing BOARD and LIBRARY headers
     std::string brdSource;  // SOURCE string as retrieved from a BOARD file
@@ -569,6 +573,8 @@ public:
     // end user must use only mm in the API.
     IDF3::IDF_UNIT GetUnit( void );
 
+    const std::string& GetNewRefDes( void );
+
     void SetBoardName( std::string aBoardName );
     const std::string& GetBoardName( void );
 
@@ -692,9 +698,7 @@ public:
 
     // returns a pointer to a component outline object or NULL
     // if the object doesn't exist
-    IDF3_COMP_OUTLINE* GetComponentOutline( const std::string aGeomName,
-                                            const std::string aPartName,
-                                            wxString aFullFileName );
+    IDF3_COMP_OUTLINE* GetComponentOutline( wxString aFullFileName );
 
     // returns a pointer to the component outline object with the
     // unique ID aComponentID
diff --git a/utils/idftools/vrml_layer.cpp b/utils/idftools/vrml_layer.cpp
index cebeb2fa97..7cb03dd54a 100644
--- a/utils/idftools/vrml_layer.cpp
+++ b/utils/idftools/vrml_layer.cpp
@@ -23,17 +23,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-/*
- * NOTES ON OUTPUT PRECISION:
- *
- * If we use %.6f then we have no need for special unit dependent formatting:
- *
- *  inch: .0254 microns
- *  mm:   0.001 microns
- *  m:    1 micron
- *
- */
-
 #include <sstream>
 #include <string>
 #include <iomanip>