diff --git a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
index 363bc55068..a0a61f01f5 100644
--- a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
+++ b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
@@ -1372,16 +1372,114 @@ wxXmlNode* PCB_IO_IPC2581::generateEcadSection()
     wxXmlNode* cadDataNode = appendNode( ecadNode, "CadData" );
     generateCadLayers( cadDataNode );
     generateDrillLayers( cadDataNode);
+    generateStackup( cadDataNode );
     generateStepSection( cadDataNode );
 
     return ecadNode;
 }
 
 
+void PCB_IO_IPC2581::generateCadSpecs( wxXmlNode* aCadLayerNode )
+{
+    BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
+    BOARD_STACKUP&         stackup = dsnSettings.GetStackupDescriptor();
+    stackup.SynchronizeWithBoard( &dsnSettings );
+
+    std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
+    std::set<PCB_LAYER_ID> added_layers;
+
+    for( int i = 0; i < stackup.GetCount(); i++ )
+    {
+        BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
+
+        for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
+        {
+            wxString ly_name = stackup_item->GetLayerName();
+
+            if( ly_name.IsEmpty() )
+            {
+                if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
+                    ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
+
+                if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
+                {
+                    ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
+
+                    if( sublayer_id > 0 )
+                        ly_name += wxString::Format( "_%d", sublayer_id );
+                }
+            }
+
+            ly_name = genString( ly_name, "SPEC_LAYER" );
+
+            wxXmlNode* specNode = appendNode( aCadLayerNode, "Spec" );
+            addAttribute( specNode,  "name", ly_name );
+            wxXmlNode* generalNode = appendNode( specNode, "General" );
+            addAttribute( generalNode,  "type", "MATERIAL" );
+            wxXmlNode* propertyNode = appendNode( generalNode, "Property" );
+
+            switch ( stackup_item->GetType() )
+            {
+                case BS_ITEM_TYPE_COPPER:
+                {
+                    addAttribute( propertyNode, "text", "COPPER" );
+                    wxXmlNode* conductorNode = appendNode( specNode, "Conductor" );
+                    addAttribute( conductorNode, "type", "CONDUCTIVITY" );
+                    propertyNode = appendNode( conductorNode, "Property" );
+                    addAttribute( propertyNode, "unit", wxT( "SIEMENS/M" ) );
+                    addAttribute( propertyNode, "value", wxT( "5.959E7" ) );
+                    break;
+                }
+                case BS_ITEM_TYPE_DIELECTRIC:
+                {
+                    addAttribute( propertyNode, "text", stackup_item->GetMaterial() );
+                    propertyNode = appendNode( generalNode, "Property" );
+                    addAttribute( propertyNode,  "text", wxString::Format( "Type : %s",
+                                                                        stackup_item->GetTypeName() ) );
+                    wxXmlNode* dielectricNode = appendNode( specNode, "Dielectric" );
+                    addAttribute( dielectricNode, "type", "DIELECTRIC_CONSTANT" );
+                    propertyNode = appendNode( dielectricNode, "Property" );
+                    addAttribute( propertyNode, "value",
+                                  floatVal( stackup_item->GetEpsilonR( sublayer_id ) ) );
+                    dielectricNode = appendNode( specNode, "Dielectric" );
+                    addAttribute( dielectricNode, "type", "LOSS_TANGENT" );
+                    propertyNode = appendNode( dielectricNode, "Property" );
+                    addAttribute( propertyNode, "value",
+                                  floatVal( stackup_item->GetLossTangent( sublayer_id ) ) );
+                    break;
+                }
+                case BS_ITEM_TYPE_SILKSCREEN:
+                    addAttribute( propertyNode,  "text", stackup_item->GetTypeName() );
+                    propertyNode = appendNode( generalNode, "Property" );
+                    addAttribute( propertyNode,  "text", wxString::Format( "Color : %s",
+                                                                        stackup_item->GetColor() ) );
+                    propertyNode = appendNode( generalNode, "Property" );
+                    addAttribute( propertyNode,  "text", wxString::Format( "Type : %s",
+                                                                        stackup_item->GetTypeName() ) );
+                    break;
+                case BS_ITEM_TYPE_SOLDERMASK:
+                    addAttribute( propertyNode,  "text", "SOLDERMASK" );
+                    propertyNode = appendNode( generalNode, "Property" );
+                    addAttribute( propertyNode,  "text", wxString::Format( "Color : %s",
+                                                                        stackup_item->GetColor() ) );
+                    propertyNode = appendNode( generalNode, "Property" );
+                    addAttribute( propertyNode,  "text", wxString::Format( "Type : %s",
+                                                                        stackup_item->GetTypeName() ) );
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
+
+
 void PCB_IO_IPC2581::addCadHeader( wxXmlNode* aEcadNode )
 {
     wxXmlNode* cadHeaderNode = appendNode( aEcadNode, "CadHeader" );
     addAttribute( cadHeaderNode,  "units", m_units_str );
+
+    generateCadSpecs( cadHeaderNode );
 }
 
 
@@ -1471,6 +1569,70 @@ void PCB_IO_IPC2581::addLayerAttributes( wxXmlNode* aNode, PCB_LAYER_ID aLayer )
 }
 
 
+void PCB_IO_IPC2581::generateStackup( wxXmlNode* aCadLayerNode )
+{
+    BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
+    BOARD_STACKUP&         stackup = dsnSettings.GetStackupDescriptor();
+    stackup.SynchronizeWithBoard( &dsnSettings );
+
+    wxXmlNode* stackupNode = appendNode( aCadLayerNode, "Stackup" );
+    addAttribute( stackupNode, "name", "Primary_Stackup" );
+    addAttribute( stackupNode, "overallThickness", floatVal( m_scale * stackup.BuildBoardThicknessFromStackup() ) );
+    addAttribute( stackupNode, "tolPlus", "0.0" );
+    addAttribute( stackupNode, "tolMinus", "0.0" );
+    addAttribute( stackupNode, "whereMeasured", "MASK" );
+
+    if( m_version > 'B' )
+        addAttribute( stackupNode, "stackupStatus", "PROPOSED" );
+
+    wxXmlNode* stackupGroup = appendNode( stackupNode, "StackupGroup" );
+    addAttribute( stackupGroup, "name", "Primary_Stackup_Group" );
+    addAttribute( stackupGroup, "thickness", floatVal( m_scale * stackup.BuildBoardThicknessFromStackup() ) );
+    addAttribute( stackupGroup, "tolPlus", "0.0" );
+    addAttribute( stackupGroup, "tolMinus", "0.0" );
+
+    std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
+    std::set<PCB_LAYER_ID> added_layers;
+
+    for( int i = 0; i < stackup.GetCount(); i++ )
+    {
+        BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
+
+        for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
+        {
+
+            wxXmlNode* stackupLayer = appendNode( stackupGroup, "StackupLayer" );
+            wxString ly_name = stackup_item->GetLayerName();
+
+            if( ly_name.IsEmpty() )
+            {
+                if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
+                    ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
+
+                if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
+                {
+                    ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
+
+                    if( sublayer_id > 0 )
+                        ly_name += wxString::Format( "_%d", sublayer_id );
+                }
+            }
+
+            ly_name = genString( ly_name, "LAYER" );
+
+            addAttribute( stackupLayer,  "layerOrGroupRef", ly_name );
+            addAttribute( stackupLayer,  "thickness", floatVal( m_scale * stackup_item->GetThickness() ) );
+            addAttribute( stackupLayer,  "tolPlus", "0.0" );
+            addAttribute( stackupLayer,  "tolMinus", "0.0" );
+            addAttribute( stackupLayer,  "sequence", wxString::Format( "%d", i ) );
+
+            wxXmlNode* specLayerNode = appendNode( stackupLayer, "SpecRef" );
+            addAttribute( specLayerNode,  "id", wxString::Format( "SPEC_%s", ly_name ) );
+        }
+    }
+}
+
+
 void PCB_IO_IPC2581::generateCadLayers( wxXmlNode* aCadLayerNode )
 {
 
@@ -1501,8 +1663,10 @@ void PCB_IO_IPC2581::generateCadLayers( wxXmlNode* aCadLayerNode )
 
                 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
                 {
-                    ly_name = wxString::Format( "DIELECTRIC_%d",
-                                                stackup_item->GetDielectricLayerId() );
+                    ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
+
+                    if( sublayer_id > 0 )
+                        ly_name += wxString::Format( "_%d", sublayer_id );
                 }
             }
 
@@ -1693,6 +1857,12 @@ void PCB_IO_IPC2581::addPadStack( wxXmlNode* aPadNode, const PAD* aPad )
     addAttribute( padStackDefNode,  "name", name );
     m_padstacks.push_back( padStackDefNode );
 
+    if( m_last_padstack )
+    {
+        insertNodeAfter( m_last_padstack, padStackDefNode );
+        m_last_padstack = padStackDefNode;
+    }
+
     // Only handle round holes here because IPC2581 does not support non-round holes
     // These will be handled in a slot layer
     if( aPad->HasDrilledHole() )
@@ -1789,12 +1959,12 @@ void PCB_IO_IPC2581::addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia )
 
 
 bool PCB_IO_IPC2581::addPolygonNode( wxXmlNode* aParentNode,
-                                     const SHAPE_POLY_SET::POLYGON& aPolygon, FILL_T aFillType,
+                                     const SHAPE_LINE_CHAIN& aPolygon, FILL_T aFillType,
                                      int aWidth, LINE_STYLE aDashType )
 {
     wxXmlNode* polygonNode = nullptr;
 
-    if( aPolygon.empty() || aPolygon[0].PointCount() < 3 )
+    if( aPolygon.PointCount() < 3 )
         return false;
 
     auto make_node =
@@ -1803,7 +1973,7 @@ bool PCB_IO_IPC2581::addPolygonNode( wxXmlNode* aParentNode,
         polygonNode = appendNode( aParentNode, "Polygon" );
         wxXmlNode* polybeginNode = appendNode( polygonNode, "PolyBegin" );
 
-        const std::vector<VECTOR2I>& pts = aPolygon[0].CPoints();
+        const std::vector<VECTOR2I>& pts = aPolygon.CPoints();
         addXY( polybeginNode, pts[0] );
 
         for( size_t ii = 1; ii < pts.size(); ++ii )
@@ -1873,14 +2043,31 @@ bool PCB_IO_IPC2581::addOutlineNode( wxXmlNode* aParentNode, const SHAPE_POLY_SE
 
     wxXmlNode* outlineNode = appendNode( aParentNode, "Outline" );
 
-    for( int ii = 0; ii < aPolySet.OutlineCount(); ++ii )
-    {
-        wxCHECK2( aPolySet.Outline( ii ).PointCount() >= 3, continue );
+    // Outlines can only have one polygon according to the IPC-2581 spec, so
+    // if there are more than one, we need to combine them into a single polygon
+    const SHAPE_LINE_CHAIN* outline = &aPolySet.Outline( 0 );
+    SHAPE_LINE_CHAIN        bbox_outline;
+    BOX2I                   bbox = outline->BBox();
 
-        if( !addPolygonNode( outlineNode, aPolySet.Polygon( ii ) ) )
-            wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to outline" ) );
+    if( aPolySet.OutlineCount() > 1 )
+    {
+        for( int ii = 1; ii < aPolySet.OutlineCount(); ++ii )
+        {
+            wxCHECK2( aPolySet.Outline( ii ).PointCount() >= 3, continue );
+            bbox.Merge( aPolySet.Outline( ii ).BBox() );
+        }
+
+        bbox_outline.Append( bbox.GetLeft(), bbox.GetTop() );
+        bbox_outline.Append( bbox.GetRight(), bbox.GetTop() );
+        bbox_outline.Append( bbox.GetRight(), bbox.GetBottom() );
+        bbox_outline.Append( bbox.GetLeft(), bbox.GetBottom() );
+        outline = &bbox_outline;
     }
 
+
+    if( !addPolygonNode( outlineNode, *outline ) )
+        wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to outline" ) );
+
     if( !outlineNode->GetChildren() )
     {
         aParentNode->RemoveChild( outlineNode );
@@ -1903,7 +2090,7 @@ bool PCB_IO_IPC2581::addContourNode( wxXmlNode* aParentNode, const SHAPE_POLY_SE
 
     wxXmlNode* contourNode = appendNode( aParentNode, "Contour" );
 
-    if( addPolygonNode( contourNode, aPolySet.Polygon( aOutline ), aFillType, aWidth, aDashType ) )
+    if( addPolygonNode( contourNode, aPolySet.Outline( aOutline ), aFillType, aWidth, aDashType ) )
     {
         // Do not attempt to add cutouts to shapes that are already hollow
         if( aFillType != FILL_T::NO_FILL )
@@ -1932,7 +2119,7 @@ void PCB_IO_IPC2581::generateProfile( wxXmlNode* aStepNode )
 
     wxXmlNode* profileNode = appendNode( aStepNode, "Profile" );
 
-    if( !addPolygonNode( profileNode, board_outline.Polygon( 0 ) ) )
+    if( !addPolygonNode( profileNode, board_outline.Outline( 0 ) ) )
     {
         wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to profile" ) );
         aStepNode->RemoveChild( profileNode );
@@ -2178,7 +2365,7 @@ wxXmlNode* PCB_IO_IPC2581::addPackage( wxXmlNode* aContentNode, FOOTPRINT* aFp )
         {
             wxXmlNode* outlineNode = insertNode( layer_nodes[layer], "Outline" );
 
-            SHAPE_POLY_SET::POLYGON outline( 1 );
+            SHAPE_LINE_CHAIN outline;
             std::vector<VECTOR2I> points( 4 );
             points[0] = bbox.GetPosition();
             points[2] = bbox.GetEnd();
@@ -2187,7 +2374,7 @@ wxXmlNode* PCB_IO_IPC2581::addPackage( wxXmlNode* aContentNode, FOOTPRINT* aFp )
             points[3].x = points[2].x;
             points[3].y = points[0].y;
 
-            outline[0].Append( points );
+            outline.Append( points );
             addPolygonNode( outlineNode, outline, FILL_T::NO_FILL, 0 );
             addLineDesc( outlineNode, 0, LINE_STYLE::SOLID );
         }
diff --git a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.h b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.h
index 73ee65cbe1..912a416f38 100644
--- a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.h
+++ b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.h
@@ -166,6 +166,10 @@ private:
 
     void generateCadLayers( wxXmlNode* aCadLayerNode );
 
+    void generateCadSpecs( wxXmlNode* aCadLayerNode );
+
+    void generateStackup( wxXmlNode* aCadLayerNode );
+
     void generateDrillLayers( wxXmlNode* aCadLayerNode );
 
     void generateStepSection( wxXmlNode* aCadNode );
@@ -218,7 +222,7 @@ private:
 
     void addFillDesc( wxXmlNode* aNode, FILL_T aFillType, bool aForce = false );
 
-    bool addPolygonNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET::POLYGON& aPolygon,
+    bool addPolygonNode( wxXmlNode* aParentNode, const SHAPE_LINE_CHAIN& aPolygon,
                          FILL_T aFillType = FILL_T::FILLED_SHAPE, int aWidth = 0,
                          LINE_STYLE aDashType = LINE_STYLE::SOLID );