Open Packaging Conventions #3. Немного об API
Пришло время познакомиться с API для манипулирования пакетами OPC. На сегодня мне известны две такие библиотеки (обе представлены Microsoft).
Ну что ж, теперь, когда мы получили общее представление о структуре пакетов в OPC, а также применили эти знания на практике, имеет смысл слегка познакомиться с API, которое имеется для манипулирования пакетами OPC.
На сегодня мне известны две такие библиотеки (обе представлены Microsoft)
● Packaging API – нативная COM-based библиотека. По всей видимости, основная её задача исходно была – служить базисом для XPS Document API
● System.IO.Packaging – managed библиотека, входящая в состав .Net начиная с версии 3.0.
Я бы хотел сегодня немного поговорить о второй: рассказать о базовом функционале для манипулирования OPC пакетами (создание/чтение компонент и управление отношениями), а также обратить внимание на несколько не очень очевидных, с моей точки зрения, моментов.
Базовые классы пространства System.IO.Packaging
Практически все задачи манипулирования OPC-пакетами решаются следующими 4-я классами:
● Package – через него идет открытие/создание пакета, создание/удаление/получение компонент и отношений уровня пакета.
● PackagePart – это компонент. Через этот класс можно узнать тип контента, прочесть содержимое компонента, а также получить список его отношений
● PackageRelationship – отношение. Этот класс содержит только тип отношения и Uri цели.
● PackUriHelper – этот класс содержит набор вспомогательных методов для манипулирования Uri отношений. Он позволяет, например, легко решить такие задачи:
- Зная имя исходного компонента и относительную ссылку в одном из отношений, получить имя конечного компонента.
- Или наоборот, зная имена 2-х компонент, получит относительную ссылку от одного к другому.
Да, все классы объявлены в сборке WindowsBase.
Создание пакетов
Думаю, особо расписывать что-то здесь нет смысла, достаточно привести пример. В качестве такового мы возьмем созданный в предыдущей статье документ с котом .
Вот так выглядит программный вариант его создания:
public void
CreateWordDocumentWithTextAndImage() imageRelationshipId); |
Большая часть примера – это объявление всевозможных констант: типов контента, типов отношений, имен компонент, …
В принципе, все довольно прозрачно. Единственное, на что хочется обратить внимание – две строки добавления отношения от главной части документа к картинке. Вот они:
var
relativeUri = PackUriHelper.GetRelativeUri(mainPartUri, imagePartUri);
mainPart.CreateRelationship(relativeUri, TargetMode.Internal, imagePartRelationshipType,
imageRelationshipId);
В первой строке мы вычисляем относительный адрес между двумя именами компонент. Вообще-то делать это не обязательно: если мы знаем имя конечной компоненты, то в качестве targetUri, можно сразу указывать его. У относительного пути есть один плюс – если перемещать некую часть контейнера (например, в отдельную ветку), а все связи внутри окажутся заданы относительными путями, то их менять не придется. Так что, это больше дело вкуса.
Вторая строка интересна тем, что при добавлении связи между главной частью документа и картинкой мы явно задаем Id отношения. Этот параметр не обязателен – id в любом случае будет присвоено. Но в нашем случае автоматическая генерация Id не удобна тем, что на отношение есть ссылка из главной части документа, а значит ее пришлось бы менять.
Чтение пакета
Теперь выполним обратную работу – найдем в документе изображения и сохраним их. В случае Word-документа (как, впрочем, и любых офисных) наиболее предпочтительный способ навигации – искать связи определенного типа и двигаться по ним. Увы, такого подхода придерживаются далеко не все (например, в формате XPS все завязано на правила именования компонентов).
Вот этот пример:
public void
ReadWordDocumentImages() |
Из интересного я бы отметил 2 вещи:
● Метод GetRelationshipsByType(), который присутствует, как у пакета, так и у компонента и возвращает все связи пакета/компонента требуемого типа.
● Для того, чтобы получить имя компонента из пути, который хранится в отношении используется метод ResolvePartUri(). Причем первый параметр у него
- либо имя компонента от которого выходит отношение;
- либо “/” – если мы смотрим на отношение уровня пакета.
Ну и наконец, смена содержимого
Редактирование содержимого компонентов
Пример еще проще предыдущих:
public void
ChangeWordDocumentMainPart() |
Из важного, только 1 строчка:
mainPartStream.SetLength(0);
- которая отбрасывает предыдущее содержимое.
Почему это важно? По умолчанию поток компонента производит запись поверх уже имеющегося контента. И если предыдущее содержимое было больше, чем новое, то в хвосте компонента у вас окажется всякий мусор. Как правило, с точки зрения потребителя, такой пакет будет безнадежно испорченным.
В заключение
Если вы внимательно посмотрели на пространство System.IO.Packaging, то уже обнаружили там много других интересных возможностей. О них мы обязательно поговорим, но чуть позже, когда узнаем немного о дополнительных возможностях стандарта Open Package Convention.
Все приведенные в статье примеры кода можно найти все там же на https://msosamples.codeplex.com в папке проекта PackagingAPI.
Источник: Блог Михаила Романова.
Источник: Блог Михаила Романова.
Комментарии 0