Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Runtime.InteropServices;
- using Autodesk.AutoCAD.ApplicationServices.Core;
- using Autodesk.AutoCAD.DatabaseServices;
- using Autodesk.AutoCAD.EditorInput;
- using Autodesk.AutoCAD.Runtime;
- using System.Collections.ObjectModel;
- namespace DgnPurger
- {
- public static class CDGNPURGE
- {
- const string dgnLsDefName = "DGNLSDEF";
- const string dgnLsDictName = "ACAD_DGNLINESTYLECOMP";
- public struct ads_name
- {
- public IntPtr a;
- public IntPtr b;
- };
- [DllImport( "acdb19.dll",
- CharSet = CharSet.Unicode,
- CallingConvention = CallingConvention.Cdecl,
- EntryPoint = "acdbHandEnt" )]
- public static extern int acdbHandEnt( string h, ref ads_name n );
- public static void PurgeDgnLinetypes( Database db, Editor ed, bool writeMessages )
- {
- using( var tr = db.TransactionManager.StartTransaction() )
- {
- // Start by getting all the "complex" DGN linetypes
- // from the linetype table
- var linetypes = CollectComplexLinetypeIds( db, tr );
- // Store a count before we start removing the ones
- // that are referenced
- var ltcnt = linetypes.Count;
- // Remove any from the "to remove" list that need to be
- // kept (as they have references from objects other
- // than anonymous blocks)
- var ltsToKeep =
- PurgeLinetypesReferencedNotByAnonBlocks( db, tr, linetypes );
- // Now we collect the DGN stroke entries from the NOD
- var strokes = CollectStrokeIds( db, tr );
- // Store a count before we start removing the ones
- // that are referenced
- var strkcnt = strokes.Count;
- // Open up each of the "keeper" linetypes, and go through
- // their data, removing any NOD entries from the "to
- // remove" list that are referenced
- PurgeStrokesReferencedByLinetypes( tr, ltsToKeep, strokes );
- // Erase each of the NOD entries that are safe to remove
- int erasedStrokes = 0;
- foreach( ObjectId id in strokes )
- {
- try
- {
- var obj = tr.GetObject( id, OpenMode.ForWrite );
- obj.Erase();
- if(
- obj.GetRXClass().Name.Equals( "AcDbLSSymbolComponent" )
- )
- {
- EraseReferencedAnonBlocks( tr, obj );
- }
- erasedStrokes++;
- }
- catch( System.Exception ex )
- {
- if( writeMessages )
- {
- if( writeMessages )
- {
- ed.WriteMessage(
- "\nUnable to erase stroke ({0}): {1}",
- id.ObjectClass.Name,
- ex.Message
- );
- }
- }
- }
- }
- // And the same for the complex linetypes
- int erasedLinetypes = 0;
- foreach( ObjectId id in linetypes )
- {
- try
- {
- var obj = tr.GetObject( id, OpenMode.ForWrite );
- obj.Erase();
- erasedLinetypes++;
- }
- catch( System.Exception ex )
- {
- if( writeMessages )
- {
- ed.WriteMessage(
- "\nUnable to erase linetype ({0}): {1}",
- id.ObjectClass.Name,
- ex.Message
- );
- }
- }
- }
- // Remove the DGN stroke dictionary from the NOD if empty
- var nod =
- (DBDictionary)tr.GetObject(
- db.NamedObjectsDictionaryId, OpenMode.ForRead
- );
- if( writeMessages )
- {
- ed.WriteMessage(
- "\nPurged {0} unreferenced complex linetype records" +
- " (of {1}).",
- erasedLinetypes, ltcnt
- );
- ed.WriteMessage(
- "\nPurged {0} unreferenced strokes (of {1}).",
- erasedStrokes, strkcnt
- );
- }
- if( nod.Contains( dgnLsDictName ) )
- {
- var dgnLsDict =
- (DBDictionary)tr.GetObject(
- (ObjectId)nod[dgnLsDictName],
- OpenMode.ForRead
- );
- if( dgnLsDict.Count == 0 )
- {
- dgnLsDict.UpgradeOpen();
- dgnLsDict.Erase();
- if( writeMessages )
- {
- ed.WriteMessage(
- "\nRemoved the empty DGN linetype stroke dictionary."
- );
- }
- }
- }
- tr.Commit();
- }
- }
- // Collect the complex DGN linetypes from the linetype table
- private static ObjectIdCollection CollectComplexLinetypeIds(
- Database db, Transaction tr
- )
- {
- var ids = new ObjectIdCollection();
- var lt =
- (LinetypeTable)tr.GetObject(
- db.LinetypeTableId, OpenMode.ForRead
- );
- foreach( var ltId in lt )
- {
- // Complex DGN linetypes have an extension dictionary
- // with a certain record inside
- var obj = tr.GetObject( ltId, OpenMode.ForRead );
- if( obj.ExtensionDictionary != ObjectId.Null )
- {
- var exd =
- (DBDictionary)tr.GetObject(
- obj.ExtensionDictionary, OpenMode.ForRead
- );
- if( exd.Contains( dgnLsDefName ) )
- {
- ids.Add( ltId );
- }
- }
- }
- return ids;
- }
- // Collect the DGN stroke entries from the NOD
- private static ObjectIdCollection CollectStrokeIds(
- Database db, Transaction tr
- )
- {
- var ids = new ObjectIdCollection();
- var nod =
- (DBDictionary)tr.GetObject(
- db.NamedObjectsDictionaryId, OpenMode.ForRead
- );
- // Strokes are stored in a particular dictionary
- if( nod.Contains( dgnLsDictName ) )
- {
- var dgnDict =
- (DBDictionary)tr.GetObject(
- (ObjectId)nod[dgnLsDictName],
- OpenMode.ForRead
- );
- foreach( var item in dgnDict )
- {
- ids.Add( item.Value );
- }
- }
- return ids;
- }
- // Remove the linetype IDs that have references from objects
- // other than anonymous blocks from the list passed in,
- // returning the ones removed in a separate list
- private static ObjectIdCollection
- PurgeLinetypesReferencedNotByAnonBlocks(
- Database db, Transaction tr, ObjectIdCollection ids
- )
- {
- var keepers = new ObjectIdCollection();
- // To determine the references from objects in the database,
- // we need to open every object. One reasonably efficient way
- // to do so is to loop through all handles in the possible
- // handle space for this drawing (starting with 1, ending with
- // the value of "HANDSEED") and open each object we can
- // Get the last handle in the db
- var handseed = db.Handseed;
- // Copy the handseed total into an efficient raw datatype
- var handseedTotal = handseed.Value;
- // Loop from 1 to the last handle (could be a big loop)
- var ename = new ads_name();
- for( long i = 1; i < handseedTotal; i++ )
- {
- // Get a handle from the counter
- var handle = Convert.ToString( i, 16 );
- // Get the entity name using acdbHandEnt()
- var res = acdbHandEnt( handle, ref ename );
- if( res != 5100 ) // RTNORM
- continue;
- // Convert the entity name to an ObjectId
- var id = new ObjectId( ename.a );
- // Open the object and check its linetype
- var obj = tr.GetObject( id, OpenMode.ForRead, true );
- var ent = obj as Entity;
- if( ent != null && !ent.IsErased )
- {
- if( ids.Contains( ent.LinetypeId ) )
- {
- // If the owner does not belong to an anonymous
- // block, then we take it seriously as a reference
- var owner =
- (BlockTableRecord)tr.GetObject(
- ent.OwnerId, OpenMode.ForRead
- );
- if(
- !owner.Name.StartsWith( "*" ) ||
- owner.Name.ToUpper() == BlockTableRecord.ModelSpace ||
- owner.Name.ToUpper().StartsWith(
- BlockTableRecord.PaperSpace
- )
- )
- {
- // Move the linetype ID from the "to remove" list
- // to the "to keep" list
- ids.Remove( ent.LinetypeId );
- keepers.Add( ent.LinetypeId );
- }
- }
- }
- }
- return keepers;
- }
- // Remove the stroke objects that have references from
- // complex linetypes (or from other stroke objects, as we
- // recurse) from the list passed in
- private static void PurgeStrokesReferencedByLinetypes(
- Transaction tr,
- ObjectIdCollection tokeep,
- ObjectIdCollection nodtoremove
- )
- {
- foreach( ObjectId id in tokeep )
- {
- PurgeStrokesReferencedByObject( tr, nodtoremove, id );
- }
- }
- // Remove the stroke objects that have references from this
- // particular complex linetype or stroke object from the list
- // passed in
- private static void PurgeStrokesReferencedByObject(
- Transaction tr, ObjectIdCollection nodIds, ObjectId id
- )
- {
- var obj = tr.GetObject( id, OpenMode.ForRead );
- if( obj.ExtensionDictionary != ObjectId.Null )
- {
- // Get the extension dictionary
- var exd =
- (DBDictionary)tr.GetObject(
- obj.ExtensionDictionary, OpenMode.ForRead
- );
- // And the "DGN Linestyle Definition" object
- if( exd.Contains( dgnLsDefName ) )
- {
- var lsdef =
- tr.GetObject(
- exd.GetAt( dgnLsDefName ), OpenMode.ForRead
- );
- // Use a DWG filer to extract the references
- var refFiler = new ReferenceFiler();
- lsdef.DwgOut( refFiler );
- // Loop through the references and remove any from the
- // list passed in
- foreach( ObjectId refid in refFiler.HardPointerIds )
- {
- if( nodIds.Contains( refid ) )
- {
- nodIds.Remove( refid );
- }
- // We need to recurse, as linetype strokes can reference
- // other linetype strokes
- PurgeStrokesReferencedByObject( tr, nodIds, refid );
- }
- }
- }
- else if(
- obj.GetRXClass().Name.Equals( "AcDbLSCompoundComponent" ) ||
- obj.GetRXClass().Name.Equals( "AcDbLSPointComponent" )
- )
- {
- // We also need to consider compound components, which
- // don't use objects in their extension dictionaries to
- // manage references to strokes...
- // Use a DWG filer to extract the references from the
- // object itself
- var refFiler = new ReferenceFiler();
- obj.DwgOut( refFiler );
- // Loop through the references and remove any from the
- // list passed in
- foreach( ObjectId refid in refFiler.HardPointerIds )
- {
- if( nodIds.Contains( refid ) )
- {
- nodIds.Remove( refid );
- }
- // We need to recurse, as linetype strokes can reference
- // other linetype strokes
- PurgeStrokesReferencedByObject( tr, nodIds, refid );
- }
- }
- }
- // Erase the anonymous blocks referenced by an object
- private static void EraseReferencedAnonBlocks(
- Transaction tr, DBObject obj
- )
- {
- var refFiler = new ReferenceFiler();
- obj.DwgOut( refFiler );
- // Loop through the references and erase any
- // anonymous block definitions
- //
- foreach( ObjectId refid in refFiler.HardPointerIds )
- {
- BlockTableRecord btr =
- tr.GetObject( refid, OpenMode.ForRead ) as BlockTableRecord;
- if( btr != null && btr.IsAnonymous )
- {
- btr.UpgradeOpen();
- btr.Erase();
- }
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment