Переместить абзац из одной презентации в другую
В этом разделе показано, как использовать классы в пакете SDK Open XML для Office для программного перемещения абзаца из одной презентации в другую.
Получение объекта PresentationDocument
В пакете SDK Open PresentationDocument XML класс представляет пакет документов презентации. Чтобы работать с документом презентации, сначала создайте экземпляр PresentationDocument
класса , а затем работайте с этим экземпляром. Чтобы создать экземпляр класса из документа, вызовите Open метод, использующий путь к файлу, и логическое значение в качестве второго параметра, чтобы указать, доступен ли документ для редактирования. Чтобы открыть документ для чтения и записи, укажите значение true
для этого параметра, как показано в следующей using
инструкции.
В этом коде sourceFile
параметр представляет собой строку, представляющую путь к файлу, из которого требуется открыть документ.
В версии 3.0.0+ Close() метод был удален в пользу использования инструкции using.
Это гарантирует автоматический Dispose() вызов метода при достижении закрывающей фигурной скобки. Блок, следующий за using
оператором , устанавливает область для объекта, созданного или именованного в инструкцииusing
, в данном случае sourceDoc
.
Базовая структура документа презентации
Базовая структура 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. |
Структура формы текстовой части
В следующем тексте из спецификации ISO/IEC 29500 представлена структура этого элемента.
Этот элемент определяет существование текста в соответствующей фигуре. Весь видимый текст и свойства видимого текста содержатся в этом элементе. В нем может быть несколько абзацев, а в них — несколько пробегов текста.
© ISO/IEC 29500: 2016
В следующей таблице представлены дочерние элементы текстовой части фигуры и описание каждого из них.
Дочерний элемент | Описание |
---|---|
bodyPr | Свойства тела |
lstStyle | Стили текстового списка |
p | Текстовые абзацы |
Следующий фрагмент схемы XML определяет содержимое этого элемента:
<complexType name="CT_TextBody">
<sequence>
<element name="bodyPr" type="CT_TextBodyProperties" minOccurs="1" maxOccurs="1"/>
<element name="lstStyle" type="CT_TextListStyle" minOccurs="0" maxOccurs="1"/>
<element name="p" type="CT_TextParagraph" minOccurs="1" maxOccurs="unbounded"/>
</sequence>
</complexType>
Механизм работы примера кода
Код в этом разделе состоит из двух методов: MoveParagraphToPresentation
и GetFirstSlide
. Первый метод принимает два строковых параметра: один представляет исходный файл, который содержит перемещаемый абзац; второй представляет конечный файл, в который абзац перемещается. Метод открывает оба файла презентации, а затем вызывает GetFirstSlide
метод для получения первого слайда в каждом файле. Затем он получает первую TextBody
фигуру на каждом слайде и первый абзац в исходной фигуре. Он выполняет а deep clone
исходного абзаца, копируя не только сам исходный Paragraph
объект, но и все, что содержится в этом объекте, включая его текст. Затем метод вставляет клонированный абзац в конечный файл и удаляет исходный абзац из исходного файла, заменяя его на абзац заполнителя. Затем измененные слайды сохраняются в обеих презентациях.
// Moves a paragraph range in a TextBody shape in the source document
// to another TextBody shape in the target document.
static void MoveParagraphToPresentation(string sourceFile, string targetFile)
{
// Open the source file as read/write.
using (PresentationDocument sourceDoc = PresentationDocument.Open(sourceFile, true))
// </Snippet1
// Open the target file as read/write.
using (PresentationDocument targetDoc = PresentationDocument.Open(targetFile, true))
{
// Get the first slide in the source presentation.
SlidePart slide1 = GetFirstSlide(sourceDoc);
// Get the first TextBody shape in it.
TextBody textBody1 = slide1.Slide.Descendants<TextBody>().First();
// Get the first paragraph in the TextBody shape.
// Note: "Drawing" is the alias of namespace DocumentFormat.OpenXml.Drawing
Drawing.Paragraph p1 = textBody1.Elements<Drawing.Paragraph>().First();
// Get the first slide in the target presentation.
SlidePart slide2 = GetFirstSlide(targetDoc);
// Get the first TextBody shape in it.
TextBody textBody2 = slide2.Slide.Descendants<TextBody>().First();
// Clone the source paragraph and insert the cloned. paragraph into the target TextBody shape.
// Passing "true" creates a deep clone, which creates a copy of the
// Paragraph object and everything directly or indirectly referenced by that object.
textBody2.Append(p1.CloneNode(true));
// Remove the source paragraph from the source file.
textBody1.RemoveChild(p1);
// Replace the removed paragraph with a placeholder.
textBody1.AppendChild(new Drawing.Paragraph());
}
}
Метод GetFirstSlide
принимает переданный PresentationDocument
объект, получает его часть представления, а затем получает идентификатор первого слайда в списке слайдов. Затем метод получает идентификатор связи слайда, получает часть слайда из идентификатора связи и возвращает часть слайда вызывающему методу.
// Get the slide part of the first slide in the presentation document.
static SlidePart GetFirstSlide(PresentationDocument presentationDocument)
{
// Get relationship ID of the first slide
PresentationPart part = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
SlideIdList slideIdList = part.Presentation.SlideIdList ?? part.Presentation.AppendChild(new SlideIdList());
SlideId slideId = part.Presentation.SlideIdList?.GetFirstChild<SlideId>() ?? slideIdList.AppendChild<SlideId>(new SlideId());
string? relId = slideId.RelationshipId;
if (relId is null)
{
throw new ArgumentNullException(nameof(relId));
}
// Get the slide part by the relationship ID.
SlidePart slidePart = (SlidePart)part.GetPartById(relId);
return slidePart;
}
Пример кода
Далее представлен полный пример кода на языках C# и Visual Basic.
// Moves a paragraph range in a TextBody shape in the source document
// to another TextBody shape in the target document.
static void MoveParagraphToPresentation(string sourceFile, string targetFile)
{
// Open the source file as read/write.
using (PresentationDocument sourceDoc = PresentationDocument.Open(sourceFile, true))
// </Snippet1
// Open the target file as read/write.
using (PresentationDocument targetDoc = PresentationDocument.Open(targetFile, true))
{
// Get the first slide in the source presentation.
SlidePart slide1 = GetFirstSlide(sourceDoc);
// Get the first TextBody shape in it.
TextBody textBody1 = slide1.Slide.Descendants<TextBody>().First();
// Get the first paragraph in the TextBody shape.
// Note: "Drawing" is the alias of namespace DocumentFormat.OpenXml.Drawing
Drawing.Paragraph p1 = textBody1.Elements<Drawing.Paragraph>().First();
// Get the first slide in the target presentation.
SlidePart slide2 = GetFirstSlide(targetDoc);
// Get the first TextBody shape in it.
TextBody textBody2 = slide2.Slide.Descendants<TextBody>().First();
// Clone the source paragraph and insert the cloned. paragraph into the target TextBody shape.
// Passing "true" creates a deep clone, which creates a copy of the
// Paragraph object and everything directly or indirectly referenced by that object.
textBody2.Append(p1.CloneNode(true));
// Remove the source paragraph from the source file.
textBody1.RemoveChild(p1);
// Replace the removed paragraph with a placeholder.
textBody1.AppendChild(new Drawing.Paragraph());
}
}
// Get the slide part of the first slide in the presentation document.
static SlidePart GetFirstSlide(PresentationDocument presentationDocument)
{
// Get relationship ID of the first slide
PresentationPart part = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
SlideIdList slideIdList = part.Presentation.SlideIdList ?? part.Presentation.AppendChild(new SlideIdList());
SlideId slideId = part.Presentation.SlideIdList?.GetFirstChild<SlideId>() ?? slideIdList.AppendChild<SlideId>(new SlideId());
string? relId = slideId.RelationshipId;
if (relId is null)
{
throw new ArgumentNullException(nameof(relId));
}
// Get the slide part by the relationship ID.
SlidePart slidePart = (SlidePart)part.GetPartById(relId);
return slidePart;
}