Создание DWF-подложки в AutoCAD лиспом
Понадобилось мне тут вставить несколько dwf (или dwfx, не столь суть важно) в текущий документ dwg. Естественно, что захотелось мне задачку решить лиспом, да без применения команд типа _.dwfattach. Вот о своих мытарствах и рассказываю чуть ниже :)
Несложные ковыряния в dwg-файле выявили, что записи о DWF(x) описаниях хранятся в словаре ACAD_DWFDEFINITIONS. Ок, проверить наличие словаря и при необходимости создать его - не вопрос. А вот дальше началось уже не очень понятное и очевидное.
Во-первых, мне никак не удавалось корректно создать запись в этом словаре. Естественно, что dwf существует, естественно, его ACAD находит. Но и все! Решения, которые мне удалось найти, рано или поздно сводятся к коду, показанному на ADN DevBlog. Код приводить не буду, покажу лучше, как я над ним измывался :)
Во-первых, я из команды сделал LISP-функцию, и, во-вторых, убрал вставку DWF в пространство модели. Ну захотелось мне такое провернуть ;)
- [LispFunction("kpblc_dwfinsert")]
- public static object lisp_dwfinsert(ResultBuffer Args)
- {
- ObjectId res = ObjectId.Null;
- Array arguments = Args.AsArray();
- string sFileName = Convert.ToString(((TypedValue)(arguments.GetValue(0))).Value);
- if (System.IO.File.Exists(sFileName))
- {
- Document doc = Application.DocumentManager.MdiActiveDocument;
- Database db = doc.Database;
- Editor ed = doc.Editor;
- using (Transaction tr = db.TransactionManager.StartTransaction())
- {
- //first check for the required Layout
- UnderlayFile layouts = UnderlayHost.DwfHost.GetFile(sFileName, "");
- UnderlayItem selectedItem = null;
- Regex rxEng = new Regex("model", RegexOptions.IgnoreCase);
- Regex rxRus = new Regex("модел", RegexOptions.IgnoreCase);
- if (layouts.Items.Count > 1)
- {
- foreach (UnderlayItem item in layouts.Items)
- {
- Match strMatchEng = rxEng.Match(item.Name);
- Match strMatchRus = rxRus.Match(item.Name);
- if ((String.Compare(item.Name.ToUpper(), "MODEL", false) == 0) || strMatchEng.Success || strMatchRus.Success)
- { selectedItem = item; break; }
- }
- }
- else
- { selectedItem = layouts.Items[0]; }
- if (selectedItem == null)
- { ed.WriteMessage("error, No page with name Model"); return null; }
- DBDictionary nod = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);
- string defDictKey = UnderlayDefinition.GetDictionaryKey(typeof(DwfDefinition));
- ObjectId defId = ObjectId.Null;
- if (!nod.Contains(defDictKey))
- {
- using (DBDictionary dict = new DBDictionary())
- {
- nod.UpgradeOpen();
- defId = nod.SetAt(defDictKey, dict);
- tr.AddNewlyCreatedDBObject(dict, true);
- }
- }
- else
- { defId = nod.GetAt(defDictKey); }
- ObjectId idDef = ObjectId.Null;
- DBDictionary dwfDict = (DBDictionary)tr.GetObject(defId, OpenMode.ForRead);
- string sDwfName = Path.GetFileNameWithoutExtension(sFileName);
- if (dwfDict.Contains(sDwfName))
- { idDef = dwfDict.GetAt(sDwfName); }
- else
- {
- using (DwfDefinition dwfDef = new DwfDefinition())
- {
- dwfDict.UpgradeOpen();
- dwfDef.SourceFileName = sFileName;
- dwfDef.SetUnderlayItem(sFileName, sFileName, selectedItem);
- idDef = dwfDict.SetAt(sDwfName, dwfDef);
- tr.AddNewlyCreatedDBObject(dwfDef, true);
- }
- }
- res = idDef;
- tr.Commit();
- }
- }
- return res;
- }
Да, я понимаю: код ужасен. Но я и на .NET не пишу :)
Тем не менее свою задачу код выполняет: при условии вызова (kpblc_dwfinsert "c:\\test01.dwf") возвращается ename-указатель на созданную запись в словаре. Если вызвать окно диспетчера внешних ссылок, то мы увидим вполне логичную картину:
- $ (kpblc_dwfinsert "c:\\test01.dwf")
А вот теперь попробуем создать вхождение dwf в текущий документ. ActiveX-методов для этого не существует, поэтому попробуем использовать обычный entmakex:
- (entmakex
- (append '((0 . "DWFUNDERLAY")
- (100 . "AcDbEntity")
- (67 . 0)
- (410 . "Model")
- (8 . "0")
- (100 . "AcDbUnderlayReference")
- )
- (list (cons 340 (kpblc_dwfinsert "c:\\test01.dwf")))
- '((10 0.0 0.0 0.0)
- (41 . 1.0)
- (42 . 1.0)
- (43 . 1.0)
- (50 . 0.0)
- (210 0.0 0.0 1.0)
- (280 . 11)
- (281 . 75)
- (282 . 25)
- )
- ) ;_ end of append
- ) ;_ end of entmakex
Так, код сработал: возвращает указатель на созданный примитив (<Entity name: 7ffffb06140>); в пространстве модели появилось вхождение dwf:
А вот в диспетчере ссылок творится что-то странное:
А если повести себя как обезьяна с гранатой и выполнить "Detach" (отключить) ссылку?
Попытки выделить вставленный dwf, описание которого отсутствует в словаре, ACAD2015 не рушит (ACAD2009 падал моментально при попытке простого выделения DWF-ссылки на чертеже), но как-то лично мне неуютно от того, что в файле dwg начинается такой бардак. Кроме того, можно в ACAD20015 скрыть отображение этой "неправильной" dwf-ссылки и потом попробовать снова ее отобразить: вместо более-менее нормального вида будет показываться черт-те что (но при этом имеющее некоторые свойства DWF-подложки).
Я обратился в ADN DevHelp, ответ был примерно следующим: "Создание DWF-подложки, используя механизм COM, на данный момент невозможно. Создание соответствующих элементов через DXF является очень громоздким и неустойчивым"
Подводя итоги, можно сказать следующее: если надо вставить DWF-подложку в документ, то либо использовать командные методы, либо полностью разрабатывать на .NET: лисп с этой задачей не справится.
Автор: Алексей Кулик, на основании материалов ADN DevBlog, статьи Как можно вставить dwf(x) в текущий документ dwg?, при содействии Александра Ривилиса.
Обсуждение: http://adn-cis.org/forum/index.php?topic=1860