Как можно вставить dwf(x) в текущий документ dwg?
Понадобилось мне тут вставить несколько dwf (или dwfx, не столь суть важно) в текущий документ dwg. Естественно, что захотелось мне задачку решить лиспом, да без применения команд типа _.dwfattach. Вот о своих мытарствах и рассказываю чуть ниже
Несложные ковыряния в dwg-файле выявили, что записи о DWF(x) описаниях хранятся в словаре ACAD_DWFDEFINITIONS. Ок, проверить наличие словаря и при необходимости создать его - не вопрос. А вот дальше началось уже не очень понятное и очевидное.
Во-первых, мне никак не удавалось корректно создать запись в этом словаре. Естественно, что dwf существует, естественно, его ACAD находит. Но и все! Решения, которые мне удалось найти, рано или поздно сводятся к коду, показанному на ADN DevBlog. Код приводить не буду, покажу лучше, как я над ним измывался
Во-первых, я из команды сделал LISP-функцию, и, во-вторых, убрал вставку DWF в пространство модели. Ну захотелось мне такое провернуть
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | [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-указатель на созданную запись в словаре. Если вызвать окно диспетчера внешних ссылок, то мы увидим вполне логичную картину:
1 2 | $ (kpblc_dwfinsert "c:\\test01.dwf") <Entity name: 7ffffb06130> |
А вот теперь попробуем создать вхождение dwf в текущий документ. ActiveX-методов для этого не существует, поэтому попробуем использовать обычный entmakex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | (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 |
Так, код сработал: возвращает указатель на созданный примитив (
А вот в диспетчере ссылок творится что-то странное:
А если повести себя как обезьяна с гранатой и выполнить "Detach" (отключить) ссылку?
Попытки выделить вставленный dwf, описание которого отсутствует в словаре, ACAD2015 не рушит (ACAD2009 падал моментально при попытке простого выделения DWF-ссылки на чертеже), но как-то лично мне неуютно от того, что в файле dwg начинается такой бардак. Кроме того, можно в ACAD20015 скрыть отображение этой "неправильной" dwf-ссылки и потом попробовать снова ее отобразить: вместо более-менее нормального вида будет показываться черт-те что (но при этом имеющее некоторые свойства DWF-подложки).
Подводя итоги, можно сказать следующее: если надо вставить DWF-подложку в документ, то либо использовать командные методы, либо полностью разрабатывать на .NET: лисп с этой задачей не справится.
либо на .net написать дополнительные lisp-функции. Собственно подобные ситуации я и имел в виду, когда создавал этот опрос: http://adn-cis.org/forum/index.php?topic=349.0
Андрей, но ведь это же костыли будут! Оно надо? Я, скорее всего, буду для этой задачи использовать c#, благо работающие примеры есть )))
>Андрей, но ведь это же костыли будут! Оно надо? Я, скорее всего, буду для этой задачи использовать c#, благо работающие примеры есть )))
Чем этот случай отличается от той ситуации, когда ты на .net недавно писал lisp-функции по взрыву\расчленению proxy? То были не костыли, а это костыли?
Конечно, костыли. У меня сейчас вообще какой-то Франкенштейн получается И лисп, и arx, и .NET - все в одном
Всем привет!
У меня получилось и через dxf создать ссылку на dwf.
Прошу прощения, код только демонстрация, не рассматриваются никакие частные случаи, т.е запускаем в пустом файле.
"D:\\test.dwf" = путь к dwf
"test_dwf" = название ссылки
Ссылка на dwf вставляется и нормально работает, но в диспетчере появляется только после сохранения файла.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
n "test_dwf"
e5 (entmakex (list '(0 . "DWFDEFINITION")
'(100 . "AcDbUnderlayDefinition")
(cons 1 f)
'(2 . "Несохраненный чертеж-Model")
'(-3 ("ACAD" (1000 . "LOAD")))
)
)
e3 (entmakex (list '(0 . "DICTIONARY")
'(102 . "{ACAD_REACTORS")
(cons 330 (NAMEDOBJDICT))
'(102 . "}")
(cons 330 (NAMEDOBJDICT))
'(100 . "AcDbDictionary")
'(280 . 0)
'(281 . 1)
(cons 3 f)
(cons 350 e5)
)
)
)
(entmod (append (entget (namedobjdict)) (list '(3 . "ACAD_DWFDEFINITIONS") (cons 350 e3))))
(setq e4 (entmakex (list '(0 . "DWFDEFINITION")
(cons 330 e3)
'(100 . "AcDbUnderlayDefinition")
(cons 1 f)
(cons 2 n)
)
)
e6 (entmakex (list '(0 . "DWFUNDERLAY")
'(100 . "AcDbEntity")
'(67 . 0)
'(410 . "Model")
'(8 . "0")
'(100 . "AcDbUnderlayReference")
(cons 340 e5)
'(10 16.3241 9.47381 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)
)
)
)
(entmod
(append (reverse (member (assoc 5 (entget e5)) (reverse (entget e5))))
(list '(102 . "{ACAD_REACTORS") (cons 330 e3) (cons 330 e6) '(102 . "}") (cons 330 e3))
(member '(100 . "AcDbUnderlayDefinition") (entget e5))
)
)
Забыл добавить, код писал в AutoCAD 2015, там же и проверял...