Про компанію Послуги Портфоліо Підтримка Відгуки клієнтів Контакнта інформація ТОВ Брутка: розробка програмного забеспечення та створення сайтів

Архів новин


Создание файлов Office Open XML

Добро пожаловать в "Пространство Office" – новую колонку, посвященную использованию системы Microsoft® Office. В этой колонке мы будем изучать возможности расширения и настройки приложений и файловых форматов системы Microsoft Office. Вместе с системой Microsoft Office 2007 корпорация Майкрософт ввела новые файловые форматы для Word, Excel® и PowerPoint®, опирающиеся на спецификацию форматов файлов Office Open XML. Документы Office, сохраняемые в этом новом формате, внедряются внутрь ZIP-архивов, или пакетов. Фактическое содержимое внутри пакета хранится в виде компонентов, называемых разделами. Части, как правило, хранятся в виде внутренних документов XML, содержимое которых структурируется в соответствии с опубликованными схемами XML.
В моей колонке Basic Instincts (Основные инстинкты) от ноября 2006 г. изложены фундаментальные принципы форматов файлов Office Open XML. Я рассматривал там также, как работать с новым пакетом API, являющимся частью Microsoft .NET Framework 3.0. Тем, для кого работа с форматами файлов Office Open XML является новым делом, прежде чем читать настоящую статью, я рекомендую ознакомиться с содержимым моей ноябрьской колонки, поскольку настоящая статья базируется на обсуждавшихся там вопросах.
Создание файла a .docx
В ноябрьской колонке "Основные инстинкты" я легко написал простейшую программу для создания файла .docx. Для анализа основных этапов я использую необходимый каркас основного кода. Сначала, используя класс Package, представляемый сборкой WindowsBase, являющейся частью .NET Framework 3.0, нужно создать новый пакетный файл. Затем необходимо создать один или несколько разделов внутри пакета и записать в них соответствующую информацию. В случае простого файла .docx требуется всего лишь создать раздел с унифицированным идентификатором ресурса (Uniform Resource Identifier, URI) /word/document.xml и записать в него содержимое WordprocessingML для создания классического документа Word "Hello World".
Помните, что всегда при создании нового раздела внутри пакета необходимо указывать тип его содержимого. Код включает в себя тип содержимого, требуемый для раздела /word/document.xml в качестве второго параметра при вызове метода CreatePart. Если при вызове этого метода пропустить тип содержимого, пакет API берет на себя добавление любого необходимого компонента в элемент описания типа содержимого с названием [Content_Types].xml, имеющемся в корне любого пакетного файла.
И последнее, на что я хотел бы обратить внимание на вызов метода CreateRelationship. Эта строка кода является критически важной, поскольку она устанавливает родительско-дочернюю взаимосвязь между пакетом и разделом /word/document.xml. В следующем разделе будет показано, насколько необходимо создание связей. Если соответствующие связи не будут созданы, Word не сможет загрузить и отобразить документ.

Подробнее о связях
Структура пакета в форматах файлов Office Open XML очень сильно зависит от связей. Как упоминалось выше, если при создании разделов не связать их с пакетом соответствующими связями, пользовательские приложения (такие как Word) не смогут их распознать. Вот почему каждый раздел должен иметь связь или цепочку связей, которые ассоциируют его с содержащим его пакетом.
Существует два типа связей. Первый тип связей – это связи, определяющие соединение пакета с его разделами верхнего уровня. На Рис. 2 показано, что типичными разделами верхнего уровня в файле .docx, созданном с помощью Microsoft Office Word 2007, являются /docProps/app.xml, /docProps/core.xml и /word/document.xml. Второй тип связей – это связи, определяющие родительско-дочерние отношения между двумя разделами в пределах одного пакета. Раздел /word/document.xml, как правило, имеет связи с несколькими дочерними разделами, такими как /word/settings.xml и /word/styles.xml.
Рис. 2. Содержимое пакета
Спецификация форматов файлов Office Open XML определяет, что каждый раздел внутри пакета должен быть ассоциирован с этим пакетом либо прямо, либо косвенно. Например, такой раздел, как /word/document.xml, связан с пакетом напрямую. Другой же раздел, /word/styles.xml, ассоциирован с пакетом косвенно, поскольку он связан с разделом верхнего уровня /word/document.xml, который, в свою очередь, уже связан с пакетом.
Следует понять очень важный принцип, в соответствии с которым пользовательское приложение должно быть способно обнаруживать любой раздел внутри пакета через перечисление его связей. Фактически, при написании собственных пользовательских приложений, читающих пакеты, созданные другими приложениями, например, средствами Word и Excel, поощряется обнаружение существующих разделов путем перечисления связей.
Рассмотрим следующий пример. Допустим, нужно написать код открытия существующего .docx-файла, чтобы вывести в окне консоли XML-содержимое раздела /word/document.xml. Здесь можно пойти простейшим путем, получив доступ к этому разделу с помощью жестко закодированных значений URI:
Dim pack As Package = Package.Open("c:\Data\Hello.docx", _
FileMode.Open, FileAccess.Read)
Dim partUri As Uri = New Uri("/word/document.xml", UriKind.Relative)
Dim part As PackagePart = pack.GetPart(partUri)
'** now add code to program against part
Этот код получает доступ к разделу /word/document.xml, который делает возможным открытие потока и чтение его содержимого. Теперь придем к той же самой цели другим способом. На этот раз я буду пересчитывать связи пакета в поисках определенного типа связи. В частности, я буду искать раздел, связанный с пакетом таким же типом связи, который используется разделом основного документа в документе Word:
Dim pack As Package = Package.Open("c:\Data\Hello.docx", _
FileMode.Open, FileAccess.Read)
Dim relationshipType As String = _
"http://schemas.openxmlformats.org" & _
"/officeDocument/2006/relationships/officeDocument"
Dim rel As PackageRelationship, partUri As Uri
For Each rel In pack.GetRelationshipsByType(relationshipType)
partUri = PackUriHelper.ResolvePartUri( _
rel.SourceUri, rel.TargetUri)
Dim part As PackagePart = pack.GetPart(partUri)
'** now add code to program against part
Next
Из вышеприведенного примера видно, что этот код вызывает метод GetRelationshipsByType объекта Package, передавая имя строки целевого типа связи. Этот метод позволяет пересчитать для данного пакета все связи определенного типа. В приведенном здесь примере обнаружения разделов с типом связи officeDocument, в пакете должен оказаться только один раздел.
После того как будет найден правильный объект PackageRelationship, код динамически строит URI для целевого раздела, используя класс PackUriHelper, предоставляемый пакетом API программного компонента .NET Framework. Можно обзавестись соответствующим объектом URI, вызвав ResolvePartUri и передав значения SourceUri и TargetUri текущего объекта PackageRelationship. После того как динамическое построение URI для целевого раздела выполнено, можно вызвать метод GetPart.
Следует также иметь в виду, что Package является объектом разового применения и обращаться с ним надо соответственно. Поэтому программа доступа к элементам, находящимся внутри пакета, должна быть структурирована в рамках инструкции Using, например, таким образом:
Using pack As Package = _
Package.Open("c:\Data\Hello.docx", _
FileMode.Open, FileAccess.Read)
'*** code to access package goes here
End Using
Посмотрим теперь, как согласуются друг с другом показанные выше разрозненные фрагменты. Этот код был написан для открытия пакета и обнаружения целевого раздела с помощью целевого типа связи. Код создает URI таким образом, чтобы он мог открыть целевой раздел и запросить потоковый доступ к его содержимому.
Стандартное приложение для просмотра пакета
Теперь, когда мы знаем, как с использованием связей обнаружить и получить доступ к содержимому разделов внутри пакета, рассмотрим эту идею несколько глубже. Как я уже упоминал, спецификация форматов файлов Office Open XML определяет, что для обнаружения всех разделов в пределах пакета должны использоваться связи. Поэтому можно написать приложение, которое обследует пакет и отображает все, находящиеся в нем разделы.
Колонка текущего месяца сопровождается еще одним стандартным приложением с названием Package Viewer, разработанным на базе Windows® Forms, которое заполняет элемент управления типа "список с древовидным отображением" узлами, показывающими пакет и все его разделы в системе иерархических связей. Если щелкнуть узел на древовидном представлении, приложение предложит дополнительную информацию о пакете и его конкретных разделах. По каждому разделу можно получить сведения о типе его содержимого, о его родителе и о типе связи, связывающем его с этим родителем.
Рис. 5. Package Viewer инспектирует разделы пакета (Щелкните изображение, чтобы увеличить его)
Package Viewer позволяет пользователю выбрать пакетный файл, используя стандартный диалог Open File. После того как пользователь выбрал файл, приложение пересчитывает все связи пакета в поисках разделов верхнего уровня.
Вызов метода GetRelationships класса Package позволяет приложению пересчитать внутри пакета все разделы верхнего уровня. Если внимательно изучить внутреннее содержимое цикла For Each, то можно увидеть, что каждая итерация для конкретного раздела верхнего уровня создает объект TreeNode, а также вызывает вспомогательный метод под названием PopulateChildPartNode, который отвечает за обнаружение связанных дочерних разделов и за создание для них объектов TreeNode. А теперь исследуем код в пределах метода PopulateChildPartNode.
Видно, что каждый объект PackagePart открывает метод GetRelationships. Это позволяет пересчитать дочерние разделы каждого раздела верхнего уровня. Однако необходимо помнить, что иерархия связей разделов может расти до любого числа уровней с дочерними разделами, имеющими дочерние разделы, имеющие дочерние разделы, и так далее. Поэтому приложение конструируется так, чтобы использовать рекурсию, в соответствии с которой метод PopulateChildPartNode мог бы вызывать сам себя. Это делает возможным написание простого метода, который мог бы "переползать" на необходимое число уровней, заполняя элемент управления "список с древовидным отображением" всеми разделами, которые могут быть найдены в рамках текущего пакета.
Если внимательно рассмотреть код приложения Package Viewer, то можно заметить, что для отслеживания различных атрибутов логических объектов, представляемых каждым из узлов, он использует определенную пользователем структуру и свойство Tag каждого объекта TreeNode. Например, имеется информация о том представляет ли каждый узел пакет, раздел или внешнюю связь. Для узлов, которые представляют разделы, имеется информация о типе содержимого раздела, его родительском элементе и типе связи, посредством которой он соединен с родительским элементом. Если щелкнуть узел на этом дереве, данная информация будет использована для заселения элементов управления на правой стороне главной формы приложения.
Package Viewer также предоставляет функциональные возможности для отображения содержимого всех XML-разделов, находящихся внутри пакета. Он осуществляет это путем повторного открытия пакета и получения потокового доступа к целевому разделу. Затем XML-содержимое раздела записывается во временный файл. Наконец, файл загружается в элемент управления WebBrowser приложения Windows Forms, который отображает XML-содержимое в цветной кодировке и стягиваемых колонках. Будем надеяться, это стандартное приложение окажется достаточно полезным при изучении разделов и XML, необходимых для работы с документами форматов файлов Office Open XML.
Теперь у нас есть представление о том, что необходимо для генерирования документов Word, Excel и PowerPoint с помощью форматов файлов Office Open XML. Теоретически все просто. Все, что необходимо сделать, это создать новый пакетный файл, добавить к нему требуемые разделы и заполнить их XML-содержимым в соответствии с надлежащими схемами XML. Но несмотря на простоту теории, освоение некоторых тонкостей займет определенное время. Потребуется также ознакомиться с соответствующими параграфами, посвященными спецификациям файловых форматов, которые можно загрузить с OpenXmlDeveloper.org.
При работе с документами Word необходимо знать, какие типы разделов входят в пакет и как они должны структурироваться по типам содержимого и связям. Кроме того, необходимо знать, как сгенерировать формат WordprocessingML, который входит в каждый из этих разделов. При работе с таблицами Excel типы содержимого и связи будут другими. Вместо формата WordprocessingML надо будет изучить и пользоваться форматом SpreadsheetML. Если нужно сгенерировать файл XML, необходимый для создания "с нуля" документов, содержащих таблицы, графики и форматирование, потребуются определенные дополнительные усилия.

Новые возможности для разработчика в Word 2007
Колонку этого месяца я хочу завершить демонстрацией способа создания профессионально оформленных документов Word 2007, для чего потребуется написать код, генерирующий формат WordprocessingML. Сначала несколько слов о двух новых возможностях Word 2007, которые могут быть применены для работы с документами, хранящимися в новых форматах файлов Office Open XML. Первая возможность представляет собой хранилище данных XML, которое позволяет внедрять в .docx-файл в качестве разделов один или несколько пользовательских документов XML. Вторая возможность – элементы управления содержанием, являющиеся элементами пользовательского интерфейса, определенными в разделе /word/document.xml, поддерживающем ввод и привязку данных.
При желании поэкспериментировать с элементами управления содержанием для начала необходимо в Word 2007 открыть диалоговое окно Word Options и на вкладке Popular убедиться, чтобы для параметра Ribbon была помечена вкладка Show Developer. На вкладке Developer будет показан набор элементов управления. Все эти элементы управления могут быть добавлены в документ Word.
Рис. 8. Элементы управления, находящиеся на вкладке Developer
Следует отметить, что элементы управления содержанием можно добавлять только в .docx-файлы Word нового формата. Они не поддерживаются .doc-файлами, поскольку не могут быть определены при помощи более старого двоичного формата. Однако при работе в новом формате элементы управления содержанием могут быть добавлены в документ Word для обеспечения пользователя элементами ввода. В частности, можно сконструировать документ Word, который запрашивает у пользователя определенную информацию для заполнения делового документа, например, календарь для выбора даты.
Обратите внимание, что элементы управления содержанием могут находиться в двух режимах: редактирование и отображение. Режим редактирования позволяет пользователю набирать текст, указывать дату или выбирать пункт раскрывающегося списка. Режим отображения оптимизирован только для просмотра и вывода на печать. Другими словами, аспекты редактирования элементов управления содержанием невидимы для пользователя после того, как их редактирование закончено и они больше не находятся в этом режиме.

Отделение данных от презентации в Word 2007
Несмотря на то что хранилище данных XML и элементы управления содержанием являются двумя различными функциями Word 2007, используемыми независимо друг от друга, они представляют собой довольно мощное средство, когда используются вместе. Например, в документ Word можно внедрить XML-файл с данными для заказчика или накладную. Затем с помощью выражений XPath можно привязать элементы управления содержанием к данным, содержащимся в XML-файле. Это, фактически, позволяет отделить данные от инструкций форматирования, указывающих Word, как их следует отображать.
Начнем с простого примера. Обучаться можно на примере образца документа Word под названием CustomerEntry.docx, находящегося в загрузке, сопровождающей колонку текущего месяца. Исследуйте его с помощью Package Viewer – это позволит быстро выяснить, как стыкуются друг с другом все его части. Однако при самостоятельном создании такого документа следует изменить расширение файла с .docx на .zip, чтобы можно было напрямую открывать пакетный файл и манипулировать его содержимым в соответствии с тем, как я это обсуждал в колонке "Основные инстинкты" в ноябре 2006 г.
Файл CustomerEntry.docx содержит простой пользовательский документ XML, который, в свою очередь, содержит данные заказчика:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Customer xmlns="http://litware.com/2006/customer">
<Name>Brian Cox</Name>
<Address>2732 Baker Blvd</Address>
<City>Eugene</City>
<State>OR</State>
<Zip>97403</Zip>
</Customer>
Этот документ XML внедряется как раздел в .docx-файл с использованием URI-раздела /customXml/item1.xml. Название этого раздела должно отличаться от названия item1.xml. Вместе с тем, желательно придерживаться схемы наименований, которую использует Word 2007 при создании и переименовании разделов в хранилище данных XML.
Далее, необходимо создать путь, идентифицирующий содержимое в документе XML заказчика. Это делается посредством определения элемента dataStoreItem с идентификатором GUID (Globally Unique Identifier). При этом необходимо создать раздел с названием /customXml/itemProps1.xml и установить связь между ним и его родительским разделом /customXml/item1.xml. Ниже приводится пример того, как должно выглядеть содержимое раздела /customXml/itemProps1.xml:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ds:dataStoreItem ds:itemID="{D5CB27EE-AE18-48C7-B53F-E921F4653E70}"
xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/
customXml">
<ds:schemaRefs>
<ds:schemaRef ds:uri="http://litware.com/2006/customer"/>
</ds:schemaRefs>
</ds:dataStoreItem>
На следующем шаге создается связь между разделами /word/document.xml и /customXml/item1.xml. Эта связь должна определять раздел /word/document.xml как родительский, а раздел /customXml/item1.xml как дочерний. Тип связи устанавливается с помощью строки:
http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml
После выполнения всех шагов по настройке пользовательского документа XML как идентифицируемого элемента dataStoreItem из раздела /word/document.xml можно получить доступ к содержащимся в нем данным. Например, используя Visual Basic® for Applications (VBA), можно написать код для документа Word 2007 с поддержкой макросов, который извлекает объект CustomXmlPart из объекта ActiveDocument, а затем извлекает необходимые заказчику данные из внедренного документа XML. В данном примере я собираюсь создать в разделе /word/document.xml элементы управления содержанием и привязать их к конкретным элементам в документе XML с данными заказчика.
К сожалению, Word 2007 не позволяет через пользовательский интерфейс привязать элементы управления содержанием к элементам хранилища данных XML. Поэтому необходимо непосредственное редактирование раздела /word/document.xml с помощью редактора XML, такого как Visual Studio® 2005. Ниже приведен пример элемента в формате WordprocessingML, который можно включить в раздел /word/document.xml с целью создания привязки к элементу Name, находящемуся внутри элемента Customer пользовательского документа XML:
<w:sdtPr>
<w:id w:val="256125470"/>
<w:dataBinding
w:prefixMappings="xmlns:ns0='http://litware.com/2006/customer'"
w:xpath="/ns0:Customer[1]/ns0:Name"
w:storeItemID="{D5CB27EE-AE18-48C7-B53F-E921F4653E70}"/>
<w:text/>
</w:sdtPr>
Элемент dataBinding содержит атрибут storeItemID, который ссылается на идентификатор GUID элемента dataStoreItem, содержащего данные заказчика. Элемент dataBinding также содержит атрибут XPath, который определяет выражение XPath для привязки элемента управления содержанием к определенному элементу внутри пользовательского файла XML. После того как в файл /word/document.xml включены все элементы dataBinding, необходимые для отображения соответствующих данных из пользовательского файла XML, можно конструировать документ Word.
Хотя может потребоваться некоторое время, чтобы почувствовать себя уверенно при ручном конструировании документов Word, содержащих пользовательские файлы XML и связанные элементы управления содержанием, потраченные усилия будут не напрасны. Это приводит к элегантному решению проблемы отделения самих данных от их презентации. После создания минимального документа Word со связанными элементами управления содержанием, всю остальную работу для придания ему профессионального вида, можно выполнять непосредственно в Word. Например, в документ можно вводить текст и знаки так же, как это может делать любой пользователь Word. Можно создавать новые таблицы и секции, и просто перетаскивать элемент

2007-03-20

 

Архів новин: новини IT, опис технологій, ціни