Guest User

Untitled

a guest
Jul 30th, 2013
187
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 11.66 KB | None | 0 0
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using Autodesk.AutoCAD.ApplicationServices.Core;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.EditorInput;
  6. using Autodesk.AutoCAD.Runtime;
  7. using System.Collections.ObjectModel;
  8.  
  9. namespace DgnPurger
  10. {
  11.   public static class CDGNPURGE
  12.   {
  13.     const string dgnLsDefName = "DGNLSDEF";
  14.     const string dgnLsDictName = "ACAD_DGNLINESTYLECOMP";
  15.  
  16.     public struct ads_name
  17.     {
  18.       public IntPtr a;
  19.       public IntPtr b;
  20.     };
  21.  
  22.     [DllImport( "acdb19.dll",
  23.       CharSet = CharSet.Unicode,
  24.       CallingConvention = CallingConvention.Cdecl,
  25.       EntryPoint = "acdbHandEnt" )]
  26.     public static extern int acdbHandEnt( string h, ref ads_name n );
  27.  
  28.     public static void PurgeDgnLinetypes( Database db, Editor ed, bool writeMessages )
  29.     {
  30.       using( var tr = db.TransactionManager.StartTransaction() )
  31.       {
  32.         // Start by getting all the "complex" DGN linetypes
  33.         // from the linetype table
  34.  
  35.         var linetypes = CollectComplexLinetypeIds( db, tr );
  36.  
  37.         // Store a count before we start removing the ones
  38.         // that are referenced
  39.  
  40.         var ltcnt = linetypes.Count;
  41.  
  42.         // Remove any from the "to remove" list that need to be
  43.         // kept (as they have references from objects other
  44.         // than anonymous blocks)
  45.  
  46.         var ltsToKeep =
  47.           PurgeLinetypesReferencedNotByAnonBlocks( db, tr, linetypes );
  48.  
  49.         // Now we collect the DGN stroke entries from the NOD
  50.  
  51.         var strokes = CollectStrokeIds( db, tr );
  52.  
  53.         // Store a count before we start removing the ones
  54.         // that are referenced
  55.  
  56.         var strkcnt = strokes.Count;
  57.  
  58.         // Open up each of the "keeper" linetypes, and go through
  59.         // their data, removing any NOD entries from the "to
  60.         // remove" list that are referenced
  61.  
  62.         PurgeStrokesReferencedByLinetypes( tr, ltsToKeep, strokes );
  63.  
  64.         // Erase each of the NOD entries that are safe to remove
  65.  
  66.         int erasedStrokes = 0;
  67.  
  68.         foreach( ObjectId id in strokes )
  69.         {
  70.           try
  71.           {
  72.             var obj = tr.GetObject( id, OpenMode.ForWrite );
  73.             obj.Erase();
  74.             if(
  75.               obj.GetRXClass().Name.Equals( "AcDbLSSymbolComponent" )
  76.             )
  77.             {
  78.               EraseReferencedAnonBlocks( tr, obj );
  79.             }
  80.             erasedStrokes++;
  81.           }
  82.           catch( System.Exception ex )
  83.           {
  84.             if( writeMessages )
  85.             {
  86.               if( writeMessages )
  87.               {
  88.                 ed.WriteMessage(
  89.                   "\nUnable to erase stroke ({0}): {1}",
  90.                   id.ObjectClass.Name,
  91.                   ex.Message
  92.                 );
  93.               }
  94.             }
  95.           }
  96.         }
  97.  
  98.         // And the same for the complex linetypes
  99.  
  100.         int erasedLinetypes = 0;
  101.  
  102.         foreach( ObjectId id in linetypes )
  103.         {
  104.           try
  105.           {
  106.             var obj = tr.GetObject( id, OpenMode.ForWrite );
  107.             obj.Erase();
  108.             erasedLinetypes++;
  109.           }
  110.           catch( System.Exception ex )
  111.           {
  112.             if( writeMessages )
  113.             {
  114.               ed.WriteMessage(
  115.                 "\nUnable to erase linetype ({0}): {1}",
  116.                 id.ObjectClass.Name,
  117.                 ex.Message
  118.               );
  119.             }
  120.           }
  121.         }
  122.  
  123.         // Remove the DGN stroke dictionary from the NOD if empty
  124.  
  125.         var nod =
  126.           (DBDictionary)tr.GetObject(
  127.             db.NamedObjectsDictionaryId, OpenMode.ForRead
  128.           );
  129.  
  130.         if( writeMessages )
  131.         {
  132.           ed.WriteMessage(
  133.             "\nPurged {0} unreferenced complex linetype records" +
  134.             " (of {1}).",
  135.             erasedLinetypes, ltcnt
  136.           );
  137.  
  138.           ed.WriteMessage(
  139.             "\nPurged {0} unreferenced strokes (of {1}).",
  140.             erasedStrokes, strkcnt
  141.           );
  142.         }
  143.  
  144.         if( nod.Contains( dgnLsDictName ) )
  145.         {
  146.           var dgnLsDict =
  147.             (DBDictionary)tr.GetObject(
  148.               (ObjectId)nod[dgnLsDictName],
  149.               OpenMode.ForRead
  150.             );
  151.  
  152.           if( dgnLsDict.Count == 0 )
  153.           {
  154.             dgnLsDict.UpgradeOpen();
  155.             dgnLsDict.Erase();
  156.             if( writeMessages )
  157.             {
  158.  
  159.               ed.WriteMessage(
  160.                 "\nRemoved the empty DGN linetype stroke dictionary."
  161.               );
  162.             }
  163.           }
  164.         }
  165.  
  166.         tr.Commit();
  167.       }
  168.     }
  169.  
  170.     // Collect the complex DGN linetypes from the linetype table
  171.  
  172.     private static ObjectIdCollection CollectComplexLinetypeIds(
  173.       Database db, Transaction tr
  174.     )
  175.     {
  176.       var ids = new ObjectIdCollection();
  177.  
  178.       var lt =
  179.         (LinetypeTable)tr.GetObject(
  180.           db.LinetypeTableId, OpenMode.ForRead
  181.         );
  182.       foreach( var ltId in lt )
  183.       {
  184.         // Complex DGN linetypes have an extension dictionary
  185.         // with a certain record inside
  186.  
  187.         var obj = tr.GetObject( ltId, OpenMode.ForRead );
  188.         if( obj.ExtensionDictionary != ObjectId.Null )
  189.         {
  190.           var exd =
  191.             (DBDictionary)tr.GetObject(
  192.               obj.ExtensionDictionary, OpenMode.ForRead
  193.             );
  194.           if( exd.Contains( dgnLsDefName ) )
  195.           {
  196.             ids.Add( ltId );
  197.           }
  198.         }
  199.       }
  200.       return ids;
  201.     }
  202.  
  203.     // Collect the DGN stroke entries from the NOD
  204.  
  205.     private static ObjectIdCollection CollectStrokeIds(
  206.       Database db, Transaction tr
  207.     )
  208.     {
  209.       var ids = new ObjectIdCollection();
  210.  
  211.       var nod =
  212.         (DBDictionary)tr.GetObject(
  213.           db.NamedObjectsDictionaryId, OpenMode.ForRead
  214.         );
  215.  
  216.       // Strokes are stored in a particular dictionary
  217.  
  218.       if( nod.Contains( dgnLsDictName ) )
  219.       {
  220.         var dgnDict =
  221.           (DBDictionary)tr.GetObject(
  222.             (ObjectId)nod[dgnLsDictName],
  223.             OpenMode.ForRead
  224.           );
  225.  
  226.         foreach( var item in dgnDict )
  227.         {
  228.           ids.Add( item.Value );
  229.         }
  230.       }
  231.  
  232.       return ids;
  233.     }
  234.  
  235.     // Remove the linetype IDs that have references from objects
  236.     // other than anonymous blocks from the list passed in,
  237.     // returning the ones removed in a separate list
  238.  
  239.     private static ObjectIdCollection
  240.       PurgeLinetypesReferencedNotByAnonBlocks(
  241.         Database db, Transaction tr, ObjectIdCollection ids
  242.       )
  243.     {
  244.       var keepers = new ObjectIdCollection();
  245.  
  246.       // To determine the references from objects in the database,
  247.       // we need to open every object. One reasonably efficient way
  248.       // to do so is to loop through all handles in the possible
  249.       // handle space for this drawing (starting with 1, ending with
  250.       // the value of "HANDSEED") and open each object we can
  251.  
  252.       // Get the last handle in the db
  253.  
  254.       var handseed = db.Handseed;
  255.  
  256.       // Copy the handseed total into an efficient raw datatype
  257.  
  258.       var handseedTotal = handseed.Value;
  259.  
  260.       // Loop from 1 to the last handle (could be a big loop)
  261.  
  262.       var ename = new ads_name();
  263.  
  264.       for( long i = 1; i < handseedTotal; i++ )
  265.       {
  266.         // Get a handle from the counter
  267.  
  268.         var handle = Convert.ToString( i, 16 );
  269.  
  270.         // Get the entity name using acdbHandEnt()
  271.  
  272.         var res = acdbHandEnt( handle, ref ename );
  273.  
  274.         if( res != 5100 ) // RTNORM
  275.           continue;
  276.  
  277.         // Convert the entity name to an ObjectId
  278.  
  279.         var id = new ObjectId( ename.a );
  280.  
  281.         // Open the object and check its linetype
  282.  
  283.         var obj = tr.GetObject( id, OpenMode.ForRead, true );
  284.         var ent = obj as Entity;
  285.         if( ent != null && !ent.IsErased )
  286.         {
  287.           if( ids.Contains( ent.LinetypeId ) )
  288.           {
  289.             // If the owner does not belong to an anonymous
  290.             // block, then we take it seriously as a reference
  291.  
  292.             var owner =
  293.               (BlockTableRecord)tr.GetObject(
  294.                 ent.OwnerId, OpenMode.ForRead
  295.               );
  296.             if(
  297.               !owner.Name.StartsWith( "*" ) ||
  298.               owner.Name.ToUpper() == BlockTableRecord.ModelSpace ||
  299.               owner.Name.ToUpper().StartsWith(
  300.                 BlockTableRecord.PaperSpace
  301.               )
  302.             )
  303.             {
  304.               // Move the linetype ID from the "to remove" list
  305.               // to the "to keep" list
  306.  
  307.               ids.Remove( ent.LinetypeId );
  308.               keepers.Add( ent.LinetypeId );
  309.             }
  310.           }
  311.         }
  312.       }
  313.       return keepers;
  314.     }
  315.  
  316.     // Remove the stroke objects that have references from
  317.     // complex linetypes (or from other stroke objects, as we
  318.     // recurse) from the list passed in
  319.  
  320.     private static void PurgeStrokesReferencedByLinetypes(
  321.       Transaction tr,
  322.       ObjectIdCollection tokeep,
  323.       ObjectIdCollection nodtoremove
  324.     )
  325.     {
  326.       foreach( ObjectId id in tokeep )
  327.       {
  328.         PurgeStrokesReferencedByObject( tr, nodtoremove, id );
  329.       }
  330.     }
  331.  
  332.     // Remove the stroke objects that have references from this
  333.     // particular complex linetype or stroke object from the list
  334.     // passed in
  335.  
  336.     private static void PurgeStrokesReferencedByObject(
  337.       Transaction tr, ObjectIdCollection nodIds, ObjectId id
  338.     )
  339.     {
  340.       var obj = tr.GetObject( id, OpenMode.ForRead );
  341.       if( obj.ExtensionDictionary != ObjectId.Null )
  342.       {
  343.         // Get the extension dictionary
  344.  
  345.         var exd =
  346.           (DBDictionary)tr.GetObject(
  347.             obj.ExtensionDictionary, OpenMode.ForRead
  348.           );
  349.  
  350.         // And the "DGN Linestyle Definition" object
  351.  
  352.         if( exd.Contains( dgnLsDefName ) )
  353.         {
  354.           var lsdef =
  355.             tr.GetObject(
  356.               exd.GetAt( dgnLsDefName ), OpenMode.ForRead
  357.             );
  358.  
  359.           // Use a DWG filer to extract the references
  360.  
  361.           var refFiler = new ReferenceFiler();
  362.           lsdef.DwgOut( refFiler );
  363.  
  364.           // Loop through the references and remove any from the
  365.           // list passed in
  366.  
  367.           foreach( ObjectId refid in refFiler.HardPointerIds )
  368.           {
  369.             if( nodIds.Contains( refid ) )
  370.             {
  371.               nodIds.Remove( refid );
  372.             }
  373.  
  374.             // We need to recurse, as linetype strokes can reference
  375.             // other linetype strokes
  376.  
  377.             PurgeStrokesReferencedByObject( tr, nodIds, refid );
  378.           }
  379.         }
  380.       }
  381.       else if(
  382.         obj.GetRXClass().Name.Equals( "AcDbLSCompoundComponent" ) ||
  383.         obj.GetRXClass().Name.Equals( "AcDbLSPointComponent" )
  384.       )
  385.       {
  386.         // We also need to consider compound components, which
  387.         // don't use objects in their extension dictionaries to
  388.         // manage references to strokes...
  389.  
  390.         // Use a DWG filer to extract the references from the
  391.         // object itself
  392.  
  393.         var refFiler = new ReferenceFiler();
  394.         obj.DwgOut( refFiler );
  395.  
  396.         // Loop through the references and remove any from the
  397.         // list passed in
  398.  
  399.         foreach( ObjectId refid in refFiler.HardPointerIds )
  400.         {
  401.           if( nodIds.Contains( refid ) )
  402.           {
  403.             nodIds.Remove( refid );
  404.           }
  405.  
  406.           // We need to recurse, as linetype strokes can reference
  407.           // other linetype strokes
  408.  
  409.           PurgeStrokesReferencedByObject( tr, nodIds, refid );
  410.         }
  411.       }
  412.     }
  413.  
  414.     // Erase the anonymous blocks referenced by an object
  415.  
  416.     private static void EraseReferencedAnonBlocks(
  417.       Transaction tr, DBObject obj
  418.     )
  419.     {
  420.       var refFiler = new ReferenceFiler();
  421.       obj.DwgOut( refFiler );
  422.  
  423.       // Loop through the references and erase any
  424.       // anonymous block definitions
  425.       //
  426.       foreach( ObjectId refid in refFiler.HardPointerIds )
  427.       {
  428.         BlockTableRecord btr =
  429.           tr.GetObject( refid, OpenMode.ForRead ) as BlockTableRecord;
  430.         if( btr != null && btr.IsAnonymous )
  431.         {
  432.           btr.UpgradeOpen();
  433.           btr.Erase();
  434.         }
  435.       }
  436.     }
  437.   }
  438. }
Advertisement
Add Comment
Please, Sign In to add comment