Логотип сайта поддержки пользователей САПРО сайте поддержки пользователей САПР Translate to:

Создание многостраничных DWF-файлов с использованием VB/VBA

Mike Tuersley,
Cadalyst

Чтобы показать, как создавать многостраничный DWF-файл, я представлю пользовательскую форму со списком всех шаблонов в текущем чертеже (рис. 1). Здесь пользователь может выбрать, какие из шаблонов будут присутствовать в финальном DWF-файле. Таким образом необходима Userform с ListBox и CommandButton. Заключительная версия кода, которая может быть загружена, будет включать больше материала, но показанная ниже достаточно полно демонстрировать функциональные возможности. Я оставил все имена по умолчанию для средств управления, но вместе с тем я установлю чтобы ListBox мог использовать переключатели и разрешу множественный выбор.


рис. 1. Окно для создания многостраничного DWF-файла
Option Explicit

Private Sub UserForm_Initialize()
  '+--- Startup
  Dim oLO as AcadLayout
  For Each oLO In ThisDrawing.Layouts
    ListBox1.AddItem oLO.Name
  Next
  'setup controls
  CommandButton1.Caption = "Print"
  ListBox1.ListStyle = fmListStyleOption
  ListBox1.MultiSelect = fmMultiSelectMulti
  Me.Caption = "xPage DWF Print Example"
End Sub


Private Sub CommandButton1_Click()
  '+--- user is printing
  Dim cLO As New Collection
  Dim iIndex As Integer
  For iIndex = 0 To Listbox1.ListCount - 1
    If ListBox1.Selected(iIndex) = True Then
      cLO.Add ListBox1.List(iIndex)
    End If
  Next
  'hide form so code can run
  Me.Hide
  'call to printing
  MultiDWFPrint cLO
  'show form again
  Me.Show
End Sub

Замечание: предпоследняя строка - запрос на печать, который будет описан ниже.

Фоновая информация в DWF-файле

Формат DWF-фйла подобен сжатому Zip-архиву. Если вы дадите DWF-файл расширение .zip, а затем попробуете открыть его используя Winzip, вы увидите серию каталогов и файлов, которые будут содержать струтуры XML-данных, графику в формате PNG и информацию мета-данных.

Файл, который представляет интерес для нас, это DSD-файл (Drawing Set Description), расположеный в папке ePlotGlobal. Этот DSD-файл содержит таблицу содержания, которая сообщает DWF-механизму AutoCAD что необходимо для многостраничного DWF-файла. AutoCAD использует этот файл чтобы обработать необходимые панели шаблонов. Ниже представлен пример DSD-файла:

[DWF6Version]
Ver = 1
[DWF6Sheet:1st Layout Name]
DWG=C: \Wilhome.dwg
Layout = 1st Layout Name

[DWF6Sheet:Nth Layout Name]
DWG=C:\ Wilhome.dwg
Layout = Nth layout Name
[Target]
Type=1
DWF=C:\Wilhome.dwf
PWD=

Обратите внимание, что DSD-файл является не чем иным как Windows INI-файлом, где данные разделены по секциям. Первые две строки должны оставаться неизменными - они указывают версию DWF-файла. последняя строка - опциональная и позволяет установить пароль, если это необходимо. Строки в середине содержат описания шаблонов, включенных в многостраничный DWF-файл. Для каждого шаблона необходима секция с указанием имени шаблона. Следующие две строки - чертежный файл и его шаблон для обработки как части DWF-файла.

Обратите внимание на то, что в DSD-файле может быть ключевых значений, чем показано выше (там показана минимальная структура, необходимая для DSD-файла). Это важно, так как следующим нашим шагом будет создание нашего собственного DSD-файла.

Создание DSD-файла

DSD-файл необходим для создания многостраничного DWF-файла. Будет ли использован существующий DSD-файл или будет создан новый как часть этого проекта должны определить вы сами.

Я буду использовать очень простой код для создания файла и добавил цикл, который будет записывать имя каждого шаблона, который выберет пользователь в ListBox нашей формы. Ниже размещен код, который я использовал:

Private Function CreateDSDFile(cLOs As Collection, _
                 sDWFFile As String) As Boolean
  Dim iCntr As Integer  'generic counter
  Dim sTemp As String   'generic temporary string
  Dim LogNum As Integer 'reference to file object
  On Error GoTo Exit_Early
  'check to see if DSD file exists and delete it if it does
  If FileLen(sDWFFile) > 0 Then Kill sDWFFile
  'create the new dsd file
  LogNum = FreeFile
  'write to the file
  Open sDWFFile For Append Access Write Lock Write As #LogNum
    Print #LogNum, "[DWF6Version]"
    Print #LogNum, "Ver = 1"
    'iterate thru the layouts collection
    For iCntr = 1 To cLOs.Count
      'grab the next layout name in the collection
      sTemp = cLOs.Item(iCntr)
      Print #LogNum, "[DWF6Sheet:" & sTemp & "]"
      Print #LogNum, "DWG=" & m_dwgName
      Print #LogNum, "Layout = " & sTemp
    Next
    Print #LogNum, "[Target]"
    Print #LogNum, "Type=1"
    Print #LogNum, "DWF=" & sDWFFile
    Print #LogNum, "PWD="
  Close #LogNum
  'send back success
  CreateDSDFile = True
  Exit Function
  
Exit_Early:
  Err.Clear
  'send back failure
  CreateDSDFile = False
End Function

Здесь я хотел использовать функцию, которая ожидает коллекцию и строку в качестве параметра. Коллекция будет содержать список имен шаблонов, которые пользователь выберет в диалоговом окне, а строка будет содержать имя DSD-файла. В этом примере, DSD-файл будет расположен в том же самом каталоге как и текущий чертеж и будет иметь то же самое имя, как и чертеж. Функция будет возвращать назад Булево значение, которое будет указывать, было ли успешно создание DSD-файла.

Сохранение DWF-файла

К сожалению, существует только один способ создания DWF-файла из AutoCAD, это использование команды Publish, что однако создает ряд проблем, потому что Publish не доступна через AutoCAD API. Первой проблемой является то, что Publish использует диалоговое окно, которое мы не можем вызвать. Если команда Publish действительно запускает свое диалоговое окно, моя программа остановиться, поскольку я не имею никакого способа связаться с помощью интерфейса с ним.

Чтобы пройти этот этап, я использовал версию Publish для командной строки: _PUBLISH. Чтобы сделать это я должен был полавить отображение файловых диалоговых окон, выключая системную переменную FILEDIA:

ThisDrawing.SetVariable "FILEDIA", 0

Следующей проблемой является вызов команды _PUBLISH. Что бы сделать это, я использовал метод SendCommand. Метод SendCommand должен всегда резервироваться как самая последняя опция потому что он создает целую кучу проблем вашим усилиям по программированию. Так как SendCommand по существу явяется методом принудительного набора команд в командной строке AutoCAD, для программы очень легко потерять фокус так как нет способа определить, когда код SendCommand завершит свою работу. VB/VBA код только передает строку в SendCommand, и продолжает свою работу, что обычно приводит к ошибке, так как код VBA выполняется быстрее, чем команда. Так что, если Вы должны использовать SendCommand, всегда делайте ее самой последней командой в программе.

Следующая проблема, которую я мог иметь происходит из-за имени файла, который я собираюсь генерировать. Если DWF-файл имеет пробелы в имени, SendCommand будет видеть, эти пробелы как Возвраты каретки и разобьет название файла на части, оказывая этим отрицательное воздействие на программу. Чтобы обойти это препятствие, я должен передать имя DWF-файла , в кавычках без VB/VBA интерпретатора, перепутывающего кавычки со строковыми индикаторами VB. Я решил передать LISP-команду в SendCommand, чем я смог связать символьные коды [CHR (*)] с моими реальными строковым переменным и обмануть интерпретатор.

Sub MultiDWFPrint (cLayOuts As Collection)
  Dim dwgName As String
  Dim dwfName As String
  Dim dsdName As String
  Dim sTxt As String
  'grab the drawing variables we need
  dwgName = ThisDrawing.FullName
  'check to make sure we have layouts
  If cLayOuts.Count > 0 Then
    'build the DSD file name
    dsdFile = Replace(dwgName, ".dwg", ".dsd")
    'build the DWF file name
    dwfFile = Replace(dwgName, ".dwg", ".dwf")
    'create the DSD file
    If CreateDSDFile(cLayOuts, dsdFile) = True Then
      'publish our multipage DWF file
      With ThisDrawing
        sTxt = Replace(dsdFile, "\", "/")
        'turn FILEDIA *OFF* to avoid dialog boxes
        .SetVariable "FILEDIA", 0
        .Regen acActiveViewport
        'Publish layouts
        .SendCommand "(command " & Chr(34) & _
                     "-PUBLISH" & Chr(34) & _
                     " " & Chr(34) & sTxt & _
                     Chr(34) & ")" & vbCr
      End With
    Else
      MsgBox "Error creating DSD file: " & dsdFile
      GoTo Leave_Now
    End If
  Else
    MsgBox "There are no valid layouts within dwg file"
  End If
  
Leave_Now:
  'clean up before leaving
  If Not cLayOuts Is Nothing Then Set cLayOuts = Nothing
'

End Sub

Обратите внимание, что строка Replace(dsdFile, "\", "/") необходима, так как LISP не будет воспринимать слеш как символ, и его необходимо заменить на обратный слеш. Это необходимо для LISP.

Обернемся назад

Теперь остается только одна проблема. Если программа успешна, моя сессия AutoCAD теперь имеет заблокированную поддержку файлового диалога! Помните, как я сказал, что, если Вы используете SendCommand, это должно быть самая последняя команда? В примере пока, это - предпоследняя команда, и Вы можете видеть как SendCommand все еще выполняется позади кода VBA, потому что Userform вновь появляется перед тем как команда _PUBLISH закончит свою работу. Я мог забыть, что я думал об этой проблеме и только ждать жалоб конечных пользователей, или я могу добавьте код к форме, чтобы закрыть событие, устанавливая FILEDIA назад в 1:

Private Sub UserForm_Terminate()
  ThisDrawing.SetVariable "FILEDIA", 1
End Sub

А если я создам приложение, которое не содержит форму? Как будет я возвращать FILEDIA назад если это имеет место? Хорошо, мы имеем множество уникальных способов обработать этот сценарий, и я оставлю Вам исследовать их. Например, можно добавить цикл, который читает переменную USER:

Dim sTest As String
Do Until sTest = "DONE"
  sTest = ThisDrawing.GetVariable("USERS2")
Loop

В этом сценарии, значение переменной USERS2 могло быть установлено через код, который добавляют к такому событию как EndCommand или EndPlot. Вы сами можете придумать, что нужно сделать чтобы решить эту проблему.



Copyright © Сайт поддержки пользователей САПР by Victor Tkachenko