Применение темы к презентации
В этом разделе показано, как использовать классы в пакете SDK Open XML для Office для программного применения темы из одной презентации к другой презентации.
Получение объекта PresentationDocument
В пакете SDK Open PresentationDocument XML класс представляет пакет документов презентации. Для работы с документом презентации необходимо сначала создать экземпляр класса PresentationDocument и затем работать с этим экземпляром. Чтобы создать экземпляр класса из документа, вызовите Open метод, использующий путь к файлу, и логическое значение в качестве второго параметра, чтобы указать, доступен ли документ для редактирования. Чтобы открыть документ в режиме доступа только для чтения, необходимо указать значение false для этого параметра. Чтобы открыть документ для доступа в режиме чтения и записи, задайте значение true для этого параметра. В следующей инструкции using открываются два файла презентации — целевая презентация, к которой следует применить тему, и исходная презентация, к которой эта тема уже применена. Файл исходной презентации открыт для доступа только в режиме чтения, а файл целевой презентации открыт для доступа в режиме чтения и записи. В этом коде параметр themePresentation является строкой, представляющей собой путь к документу исходной презентации; параметр presentationFile является строкой, которая представляет собой путь к документу целевой презентации.
using (PresentationDocument themeDocument = PresentationDocument.Open(themePresentation, false))
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
В версии 3.0.0+ Close() метод был удален в пользу использования инструкции using.
Это гарантирует автоматический Dispose() вызов метода при достижении закрывающей фигурной скобки. Блок, следующий за using
оператором , устанавливает область для объекта, созданного или именованного в операторе using
, в данном случае themeDocument
и presentationDocument
.
Базовая структура документа презентации
Базовая структура PresentationML
документа состоит из нескольких частей, среди которых есть main часть, содержащая определение презентации. В следующем тексте из спецификации ISO/IEC 29500 представлена общая форма PresentationML
пакета.
Main часть
PresentationML
пакета начинается с корневого элемента презентации. Этот элемент содержит презентацию, которая, в свою очередь, ссылается на список слайдов, список образцов слайдов, список образцов заметок и список образцов раздаточных материалов. Список слайдов ссылается на все слайды в презентации; список образцов слайдов ссылается на все образцы слайдов, используемые в презентации; в списке образцов заметок содержатся данные о форматировании страниц заметок, а в списке образцов раздаточных материалов описан внешний вид раздаточных материалов.Раздаточные материалы представляют собой набор распечатанных слайдов, которые можно раздать слушателям для последующего использования.
Наряду с текстом и изображениями слайды могут содержать комментарии, заметки и разметку, а также могут входить в одну или несколько пользовательских презентаций. Комментарий представляет собой примечание, которое адресовано сотруднику, ответственному за обслуживание набора слайдов. Заметка представляет собой напоминание или отрывок текста, предназначенный для докладчика или для слушателей.
Другие функции документа
PresentationML
могут включать следующие: анимацию, звук, видео и переходы между слайдами.Документ
PresentationML
не хранится в виде одного большого текста в одной части. Элементы с определенной группировкой функций хранятся в различных частях. Например, все авторы в документе хранятся в одной части авторов, а каждый слайд имеет свою собственную часть.ISO/IEC 29500: 2016
Указанный ниже пример кода XML описывает презентацию, содержащую 2 слайда с идентификаторами 267 и 256.
<p:presentation xmlns:p="…" … >
<p:sldMasterIdLst>
<p:sldMasterId
xmlns:rel="https://…/relationships" rel:id="rId1"/>
</p:sldMasterIdLst>
<p:notesMasterIdLst>
<p:notesMasterId
xmlns:rel="https://…/relationships" rel:id="rId4"/>
</p:notesMasterIdLst>
<p:handoutMasterIdLst>
<p:handoutMasterId
xmlns:rel="https://…/relationships" rel:id="rId5"/>
</p:handoutMasterIdLst>
<p:sldIdLst>
<p:sldId id="267"
xmlns:rel="https://…/relationships" rel:id="rId2"/>
<p:sldId id="256"
xmlns:rel="https://…/relationships" rel:id="rId3"/>
</p:sldIdLst>
<p:sldSz cx="9144000" cy="6858000"/>
<p:notesSz cx="6858000" cy="9144000"/>
</p:presentation>
С помощью пакета SDK Open XML можно создавать структуру документа и содержимое с помощью строго типизированных классов, соответствующих элементам PresentationML. Эти классы можно найти в пространстве имен. В следующей таблице перечислены имена классов, которые соответствуют sld
элементам , sldLayout
, sldMaster
и notesMaster
.
Элемент PresentationML | Класс пакета SDK Open XML | Описание |
---|---|---|
<sld/> |
Slide | Слайд презентации. Это корневой элемент части SlidePart. |
<sldLayout/> |
SlideLayout | Разметка слайда. Это корневой элемент части SlideLayoutPart. |
<sldMaster/> |
SlideMaster | Образец слайда. Это корневой элемент части SlideMasterPart. |
<notesMaster/> |
NotesMaster | Образец заметок (или handoutMaster). Это корневой элемент части NotesMasterPart. |
Структура элемента Theme
Приведенные ниже сведения из спецификации ISO/IEC 29500 могут быть полезны при работе с этим элементом.
Данный элемент определяет сложный тип корневого уровня, связанный с таблицей общих стилей (или темой). Данный элемент содержит все различные параметры форматирования, предоставляемые для документа посредством темы; и определяет общий вид и структуру документа при использовании объектов темы в этом документе. [Пример. Рассмотрим следующее изображение в качестве примера различных тем, применяемых к презентации. В этом примере показано, как тема может влиять на шрифт, цвета, фон, заливку и эффекты для различных объектов в презентации. конец примера]
В этом примере показано, как влияют темы на шрифты, цвета, фоновые изображения, заливку и эффекты для различных объектов презентации. конец примера
© ISO/IEC 29500: 2016
В следующей таблице приведены возможные типы дочерних объектов класса Theme.
Элемент PresentationML | Класс пакета SDK Open XML | Описание |
---|---|---|
custClrLst | CustomColorList | Пользовательский список цветов |
extLst | ExtensionList | Список расширений |
extraClrSchemeLst | ExtraColorSchemeList | Список цветов дополнительной цветовой схемы |
objectDefaults | ObjectDefaults | Настройки объекта по умолчанию |
themeElements | ThemeElements | Элементы темы |
Следующий фрагмент схемы XML определяет четыре части элемента темы. Элемент themeElements содержит основные параметры форматирования, определенные в теме. Остальные части представляют собой переопределения, настройки по умолчанию и добавления данных, содержащихся в themeElements. Сложный тип, определяющий тему , CT_OfficeStyleSheet, определяется следующим образом.
<complexType name="CT_OfficeStyleSheet">
<sequence>
<element name="themeElements" type="CT_BaseStyles" minOccurs="1" maxOccurs="1"/>
<element name="objectDefaults" type="CT_ObjectStyleDefaults" minOccurs="0" maxOccurs="1"/>
<element name="extraClrSchemeLst" type="CT_ColorSchemeList" minOccurs="0" maxOccurs="1"/>
<element name="custClrLst" type="CT_CustomColorList" minOccurs="0" maxOccurs="1"/>
<element name="extLst" type="CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
</sequence>
<attribute name="name" type="xsd:string" use="optional" default=""/>
</complexType>
В данном сложном типе также содержится элемент CT_OfficeArtExtensionList, используемый для обеспечения дальнейшей расширяемости этого сложного типа.
Механизм работы примера кода
Пример кода включает две перегрузки методов ApplyThemeToPresentation и GetSlideLayoutType. В следующем примере кода показан первый перегруженный метод, в котором выполняется открытие и передача двух файлов презентации (themePresentation и presentationFile) во второй перегруженный метод в качестве параметров.
// Apply a new theme to the presentation.
static void ApplyThemeToPresentation(string presentationFile, string themePresentation)
{
using (PresentationDocument themeDocument = PresentationDocument.Open(themePresentation, false))
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
ApplyThemeToPresentationDocument(presentationDocument, themeDocument);
}
}
Во втором перегруженном методе код начинается с проверки, является ли какой-либо из файлов презентации пустыми, и в этом случае возникает исключение. Затем код получает часть презентации документа презентации, объявив объект PresentationPart и задав его равным части презентации переданного целевого объекта PresentationDocument . Затем он получает master части слайда из частей презентации обоих переданных объектов и получает идентификатор связи слайда master части целевой презентации.
// Apply a new theme to the presentation.
static void ApplyThemeToPresentationDocument(PresentationDocument presentationDocument, PresentationDocument themeDocument)
{
// Get the presentation part of the presentation document.
PresentationPart presentationPart = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
// Get the existing slide master part.
SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.ElementAt(0);
string relationshipId = presentationPart.GetIdOfPart(slideMasterPart);
// Get the new slide master part.
PresentationPart themeDocPresentationPart = themeDocument.PresentationPart ?? themeDocument.AddPresentationPart();
SlideMasterPart newSlideMasterPart = themeDocPresentationPart.SlideMasterParts.ElementAt(0);
Затем код удаляет часть существующей темы и часть образца слайда целевой презентации. Путем повторного использования предыдущего идентификатора связи он также добавляет новую часть образца слайда из исходной презентации в целевую. Он также добавляет часть темы в целевую презентацию.
if (presentationPart.ThemePart is not null)
{
// Remove the existing theme part.
presentationPart.DeletePart(presentationPart.ThemePart);
}
// Remove the old slide master part.
presentationPart.DeletePart(slideMasterPart);
// Import the new slide master part, and reuse the old relationship ID.
newSlideMasterPart = presentationPart.AddPart(newSlideMasterPart, relationshipId);
if (newSlideMasterPart.ThemePart is not null)
{
// Change to the new theme part.
presentationPart.AddPart(newSlideMasterPart.ThemePart);
}
Код выполняет итерацию по всем частям разметки слайда в части образца слайда и добавляет их в список новых разметок слайдов. Код определяет тип разметки по умолчанию. В этом примере вставляется код типа разметки по умолчанию "Title and Content".
Dictionary<string, SlideLayoutPart> newSlideLayouts = new Dictionary<string, SlideLayoutPart>();
foreach (var slideLayoutPart in newSlideMasterPart.SlideLayoutParts)
{
string? newLayoutType = GetSlideLayoutType(slideLayoutPart);
if (newLayoutType is not null)
{
newSlideLayouts.Add(newLayoutType, slideLayoutPart);
}
}
string? layoutType = null;
SlideLayoutPart? newLayoutPart = null;
// Insert the code for the layout for this example.
string defaultLayoutType = "Title and Content";
Код выполняет итерацию по всем частям слайдов в целевой презентации и удаляет связь разметки слайда для всех слайдов. Код использует метод GetSlideLayoutType для поиска типа разметки части разметки слайда. Для любого слайда с существующей частью разметки слайда добавьте новую часть разметки слайда такого же типа, который использовался ранее. Для любого слайда без существующей части разметки слайда добавьте новую часть разметки слайда типа по умолчанию.
// Remove the slide layout relationship on all slides.
foreach (var slidePart in presentationPart.SlideParts)
{
layoutType = null;
if (slidePart.SlideLayoutPart is not null)
{
// Determine the slide layout type for each slide.
layoutType = GetSlideLayoutType(slidePart.SlideLayoutPart);
// Delete the old layout part.
slidePart.DeletePart(slidePart.SlideLayoutPart);
}
if (layoutType is not null && newSlideLayouts.TryGetValue(layoutType, out newLayoutPart))
{
// Apply the new layout part.
slidePart.AddPart(newLayoutPart);
}
else
{
newLayoutPart = newSlideLayouts[defaultLayoutType];
// Apply the new default layout part.
slidePart.AddPart(newLayoutPart);
}
}
Для получения типа разметки слайда код использует метод GetSlideLayoutType, который принимает часть разметки слайда в качестве параметра и возвращает второй перегруженный метод ApplyThemeToPresentation в виде строки, представляющей имя типа разметки слайда
// Get the slide layout type.
static string? GetSlideLayoutType(SlideLayoutPart slideLayoutPart)
{
CommonSlideData? slideData = slideLayoutPart.SlideLayout?.CommonSlideData;
return slideData?.Name;
}
Пример кода
Далее приведен полный пример кода, выполняющего копирование темы из одной презентации в другую. Чтобы использовать эту программу, необходимо создать две презентации — исходную презентацию, содержащую тему, которая будет копироваться, например Myppt9-theme.pptx; другая презентация является целевой, например Myppt9.pptx. Для выполнения копирования можно использовать следующий вызов в своей программе.
ApplyThemeToPresentation(args[0], args[1]);
После выполнения этого вызова можно проверить файл Myppt2.pptx — там должна быть применена та же тема, что и в файле Myppt9-theme.pptx.
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using System.Collections.Generic;
using System.Linq;
ApplyThemeToPresentation(args[0], args[1]);
// Apply a new theme to the presentation.
static void ApplyThemeToPresentation(string presentationFile, string themePresentation)
{
using (PresentationDocument themeDocument = PresentationDocument.Open(themePresentation, false))
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
ApplyThemeToPresentationDocument(presentationDocument, themeDocument);
}
}
// Apply a new theme to the presentation.
static void ApplyThemeToPresentationDocument(PresentationDocument presentationDocument, PresentationDocument themeDocument)
{
// Get the presentation part of the presentation document.
PresentationPart presentationPart = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
// Get the existing slide master part.
SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.ElementAt(0);
string relationshipId = presentationPart.GetIdOfPart(slideMasterPart);
// Get the new slide master part.
PresentationPart themeDocPresentationPart = themeDocument.PresentationPart ?? themeDocument.AddPresentationPart();
SlideMasterPart newSlideMasterPart = themeDocPresentationPart.SlideMasterParts.ElementAt(0);
if (presentationPart.ThemePart is not null)
{
// Remove the existing theme part.
presentationPart.DeletePart(presentationPart.ThemePart);
}
// Remove the old slide master part.
presentationPart.DeletePart(slideMasterPart);
// Import the new slide master part, and reuse the old relationship ID.
newSlideMasterPart = presentationPart.AddPart(newSlideMasterPart, relationshipId);
if (newSlideMasterPart.ThemePart is not null)
{
// Change to the new theme part.
presentationPart.AddPart(newSlideMasterPart.ThemePart);
}
Dictionary<string, SlideLayoutPart> newSlideLayouts = new Dictionary<string, SlideLayoutPart>();
foreach (var slideLayoutPart in newSlideMasterPart.SlideLayoutParts)
{
string? newLayoutType = GetSlideLayoutType(slideLayoutPart);
if (newLayoutType is not null)
{
newSlideLayouts.Add(newLayoutType, slideLayoutPart);
}
}
string? layoutType = null;
SlideLayoutPart? newLayoutPart = null;
// Insert the code for the layout for this example.
string defaultLayoutType = "Title and Content";
// Remove the slide layout relationship on all slides.
foreach (var slidePart in presentationPart.SlideParts)
{
layoutType = null;
if (slidePart.SlideLayoutPart is not null)
{
// Determine the slide layout type for each slide.
layoutType = GetSlideLayoutType(slidePart.SlideLayoutPart);
// Delete the old layout part.
slidePart.DeletePart(slidePart.SlideLayoutPart);
}
if (layoutType is not null && newSlideLayouts.TryGetValue(layoutType, out newLayoutPart))
{
// Apply the new layout part.
slidePart.AddPart(newLayoutPart);
}
else
{
newLayoutPart = newSlideLayouts[defaultLayoutType];
// Apply the new default layout part.
slidePart.AddPart(newLayoutPart);
}
}
}
// Get the slide layout type.
static string? GetSlideLayoutType(SlideLayoutPart slideLayoutPart)
{
CommonSlideData? slideData = slideLayoutPart.SlideLayout?.CommonSlideData;
return slideData?.Name;
}