Make XRef path relative

Make XRef path relative

WPerciful
Advocate Advocate
643 Views
7 Replies
Message 1 of 8

Make XRef path relative

WPerciful
Advocate
Advocate

I can't seem to figure out how to make the xref file path relative.

 

 

 

 

    Public Sub ProjectCloseOut(Directory_DrawingName As String, ByVal FileType As String)
        Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
        Dim db As Database = New Database(False, True)
        Dim XrGraph As XrefGraph = db.GetHostDwgXrefGraph(False)
        XrGraph = db.GetHostDwgXrefGraph(True)
        Dim root As GraphNode = XrGraph.RootNode
        Dim r As New List(Of String)
        Using db
            Try 'FileOpenMode.OpenForReadAndAllShare
                db.ReadDwgFile(Directory_DrawingName, FileOpenMode.OpenForReadAndAllShare, False, Nothing)
            Catch __unusedException1__ As System.Exception
                ed.WriteMessage(vbLf & "Unable To read drawing file.")
            End Try
            Using tran = db.TransactionManager.StartTransaction
                For o As Integer = 0 To root.NumOut - 1
                    Dim child As XrefGraphNode = TryCast(root.Out(o), XrefGraphNode)
                    Select Case child.XrefStatus
                        Case XrefStatus.Resolved
                            '   Make all of the xrefs relitive paths
                            Dim XrefFullFileName = child.Database.Filename
                            Dim XrefFilePath = IO.Path.GetDirectoryName(AecFullFileName)
                            Dim XrefFileName = IO.Path.GetFileNameWithoutExtension(AecFullFileName)
                            dtProjectCloseOutFiles.Rows.Add(XrefFullFileName, XrefFilePath, XrefFileName, FileType)
                        Case XrefStatus.FileNotFound Or XrefStatus.Unloaded Or XrefStatus.Unreferenced
                            '   xrefs for all model files in 3D Model folder and detach them
                            Dim XrefFullFileName = child.Database.Filename
                            Dim XrefFilePath = IO.Path.GetDirectoryName(AecFullFileName)
                            Dim XrefFileName = IO.Path.GetFileNameWithoutExtension(AecFullFileName)
                            Dim acXrefId As ObjectId = db.AttachXref(XrefFullFileName, XrefFileName)
                            db.DetachXref(acXrefId)
                    End Select
                Next
            End Using
        End Using
    End Sub

 

 

Same code in C#

 

public void ProjectCloseOut(string Directory_DrawingName, string FileType)
{
    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    Database db = new Database(false, true);
    XrefGraph XrGraph = db.GetHostDwgXrefGraph(false);
    XrGraph = db.GetHostDwgXrefGraph(true);
    GraphNode root = XrGraph.RootNode;
    List<string> r = new List<string>();
    using (db)
    {
        try // FileOpenMode.OpenForReadAndAllShare
        {
            db.ReadDwgFile(Directory_DrawingName, FileOpenMode.OpenForReadAndAllShare, false, null/* TODO Change to default(_) if this is not a reference type */);
        }
        catch (Exception __unusedException1__)
        {
            ed.WriteMessage(Constants.vbLf + "Unable To read drawing file.");
        }
        using (var tran = db.TransactionManager.StartTransaction)
        {
            for (int o = 0; o <= root.NumOut - 1; o++)
            {
                XrefGraphNode child = root.Out(o) as XrefGraphNode;
                switch (child.XrefStatus)
                {
                    case object _ when XrefStatus.Resolved:
                        {
                            // Make all of the xrefs relitive paths
                            var XrefFullFileName = child.Database.Filename;
                            var XrefFilePath = System.IO.Path.GetDirectoryName(AecFullFileName);
                            var XrefFileName = System.IO.Path.GetFileNameWithoutExtension(AecFullFileName);
                            dtProjectCloseOutFiles.Rows.Add(XrefFullFileName, XrefFilePath, XrefFileName, FileType);
                            break;
                        }

                    case object _ when XrefStatus.FileNotFound | XrefStatus.Unloaded | XrefStatus.Unreferenced:
                        {
                            // xrefs for all model files in 3D Model folder and detach them
                            var XrefFullFileName = child.Database.Filename;
                            var XrefFilePath = System.IO.Path.GetDirectoryName(AecFullFileName);
                            var XrefFileName = System.IO.Path.GetFileNameWithoutExtension(AecFullFileName);
                            ObjectId acXrefId = db.AttachXref(XrefFullFileName, XrefFileName);
                            db.DetachXref(acXrefId);
                            break;
                        }
                }
            }
        }
    }
}


Wayne Perciful
Founder | Perciful Consulting LLC
Perciful Consulting

0 Likes
644 Views
7 Replies
Replies (7)
Message 2 of 8

Ed__Jobe
Mentor
Mentor

You can remove the paths using the Xref Manager that ships with AutoCAD. Just load the dwgs to be changed and change the path to ".\"

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes
Message 3 of 8

WPerciful
Advocate
Advocate

I've got over 1,000 files at a time that I need to perform this on.  Which is why I'm attempting to automate the task.



Wayne Perciful
Founder | Perciful Consulting LLC
Perciful Consulting

0 Likes
Message 4 of 8

jabowabo
Mentor
Mentor

I think @Ed__Jobe is referring to AdRefMan.exe which will batch rename xrefs:

C:\Program Files\Autodesk\AutoCAD <version>\AdRefMan.exe

 

If you still want to code it, this may help. There's some custom method calls but should be easy to figure out.

public static void MakeXrefPathsRelative()
{
	ObjectIdCollection objIdColl = new ObjectIdCollection();

	string nl = Environment.NewLine;

	// set flags for openFileDialog and select dwgs
	string defaultFolder = @"S:\Drafting";
	OfdFlags ofdFlags = OfdFlags.DefaultIsFolder | OfdFlags.AllowMultiple;
	List<string> dwgFilePaths = FileTools.GetDwgFileNames(defaultFolder, ofdFlags);
	if (dwgFilePaths == null || dwgFilePaths.Count == 0)
		return;

	foreach (string dwgFilePath in dwgFilePaths)
	{
		// save old database
		Database prevDb = HostApplicationServices.WorkingDatabase;

		using (Database dwgDb = new Database(false, true))
		{
			dwgDb.ReadDwgFile(dwgFilePath, FileShare.Read, true, "");
			dwgDb.CloseInput(true);

			// set the dwg as active
			HostApplicationServices.WorkingDatabase = dwgDb;

			using (Transaction tr = dwgDb.TransactionManager.StartTransaction())
			{
				BlockTable bt = tr.GetObject(dwgDb.BlockTableId, OpenMode.ForRead) as BlockTable;
				foreach (ObjectId btrId in bt)
				{
					BlockTableRecord btr = tr.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;
					if (btr.IsFromExternalReference)
					{
						btr.UpgradeOpen();
						String oldXrefPath = btr.PathName;

						Uri path1 = new Uri(dwgFilePath);
						Uri path2 = new Uri(oldXrefPath);
						Uri diff = path1.MakeRelativeUri(path2);
						string relPath = diff.OriginalString;
						string newXrefPath = relPath.Replace(@"%20", " ");
						btr.PathName = newXrefPath;
						objIdColl.Add(btrId);
						Debug.AppendLine("Old Path : {oldXrefPath} New Path : {newXrefPath}");
					}
				}

				tr.Commit();
			}// end transaction

			// reset to prev db it back ASAP
			HostApplicationServices.WorkingDatabase = prevDb;
			dwgDb.SaveAs(dwgFilePath, dwgDb.GetThisDwgVersion());
		}// end using dwgDb
	}// end foreach sourceFile

	if (Debug.Enabled)
		Debug.Dump("ModifyXrefPathLogFile", false, true);
}
Message 5 of 8

Ed__Jobe
Mentor
Mentor

@jabowabo wrote:

I think @Ed__Jobe is referring to AdRefMan.exe which will batch rename xrefs:

C:\Program Files\Autodesk\AutoCAD <version>\AdRefMan.exe


@WPerciful Yes, you can access it from the Start Menu. I've loaded hundreds of files at a time. It's very capable of doing it in batch mode. Do Ctrl+A to select all the files and you're good to go. Even if you bread up the 1000 into a few groups of 200-300, it'll  be quicker than writing your own app. You could have been done by now. 🙂

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

Message 6 of 8

kerry_w_brown
Advisor
Advisor

@jabowabo 

nice solution.

//-----------------

.NET core 2.0  onwards has a Path.GetRelativePath(String, String) Method

https://learn.microsoft.com/en-us/dotnet/api/system.io.path.getrelativepath?view=netcore-2.0

. . but oficially we can't use that yet.

 

Regards,


// Called Kerry or kdub in my other life.

Everything will work just as you expect it to, unless your expectations are incorrect. ~ kdub
Sometimes the question is more important than the answer. ~ kdub

NZST UTC+12 : class keyThumper<T> : Lazy<T>;      another  Swamper
Message 7 of 8

WPerciful
Advocate
Advocate

Still working on figuring out what you shared.  Haven't gotten anywhere with it as yet.  Can you add some comments to your code?



Wayne Perciful
Founder | Perciful Consulting LLC
Perciful Consulting

0 Likes
Message 8 of 8

jabowabo
Mentor
Mentor

@WPerciful wrote:

Still working on figuring out what you shared.  Haven't gotten anywhere with it as yet.  Can you add some comments to your code?


Here's a cleaned up version. This one acts on the current document.

public static void MakeXrefPathsRelative()
{
	var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
	var db = doc.Db;

	var thisDwgPath = db.OriginalFileName;

	using (DocumentLock docLock = doc.LockDocument())
	{
		using (Transaction tr = db.TransactionManager.StartTransaction())
		{
			BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;

			// iterate all bt objects
			foreach (ObjectId btrId in bt)
			{
				BlockTableRecord btr = tr.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;

				// check if object is xref
				if (btr.IsFromExternalReference)
				{
					// open for write
					btr.UpgradeOpen();

					// orig path
					String oldXrefPath = btr.PathName;
					
					// make new relative path
					Uri path1 = new Uri(thisDwgPath);
					Uri path2 = new Uri(oldXrefPath);
					Uri diff = path1.MakeRelativeUri(path2);
					string relPath = diff.OriginalString;
					string newXrefPath = relPath.Replace(@"%20", " ");

					// assign new path
					btr.PathName = newXrefPath;
					Debug.AppendLine("Old Path : {oldXrefPath} New Path : {newXrefPath}");
				}
			}

			tr.Commit();
		}// end transaction
	}// end docLock
}
0 Likes