Заменить части стилей в текстовом редакторе
В этом разделе показано, как использовать классы в пакете SDK Open XML для Office для программной замены стилей в текстовом документе стилями из другого текстового документа. Он содержит пример ReplaceStyles
метода для иллюстрации этой задачи, а также вспомогательные ReplaceStylesPart
методы и ExtractStylesPart
.
Сведения о хранилище стилей
Пакет текстовых документов, например файл с расширением .docx, на самом деле представляет собой файл .zip, состоящий из нескольких частей. Каждую часть можно представить как внешний файл. Часть имеет определенный тип контента и может содержать содержимое, равное содержимому внешнего XML-файла, двоичного файла, файла изображений и т. д. в зависимости от типа. Стандарт, который определяет, как открывать документы Open XML, хранимые в ZIP-файлах, называется Open Packaging Conventions. Дополнительные сведения о стандарте Open Packaging Conventions см. в спецификации ISO/IEC 29500-2.
Стили хранятся в выделенных частях в пакете текстовых документов. Документ Microsoft Word 2010, русская версия содержит одну часть стилей. Более поздние версии Microsoft Word добавить вторую часть stylesWithEffects. На следующем рисунке из документа Обозреватель в средстве open XML SDK Productivity Tool для Microsoft Office показаны части документа в примере документа Word 2013 и более поздних версий, содержащего стили.
Рис. 1. Стили в текстовом документе
Чтобы обеспечить "круговую передачу" документа с Word 2013 по Word 2010 и обратно, Word 2013+ сохраняет как часть исходных стилей, так и новые стили. (Спецификация форматов файлов Open XML в Office требует, чтобы корпорация Майкрософт Word игнорировать все части, которые она не распознает. Word 2010 не замечает часть stylesWithEffects, добавленную в документ Word 2013 и более поздних версий.)
Пример кода, приведенный в этом разделе, можно использовать для замены этих частей стилей.
Метод ReplaceStyles
С помощью ReplaceStyles
примера метода можно заменить стили в текстовом документе стилями в другом текстовом документе. Метод ReplaceStyles
принимает два параметра: первый параметр содержит строку, указывающую путь к файлу, который содержит стили для извлечения. Второй параметр содержит строку, указывающую путь к файлу, в который копируются стили, фактически полностью заменяя стили.
static void ReplaceStyles(string fromDoc, string toDoc)
Полный список кода для ReplaceStyles
метода и его вспомогательных методов можно найти в разделе Пример кода .
Вызов примера метода
Чтобы вызвать пример метода, передайте строку для первого параметра, указывающего путь к файлу со стилями для извлечения, и строку для второго параметра, представляющую путь к файлу, в котором необходимо заменить стили. Следующий код показывает пример использования: После завершения выполнения кода стили в целевом документе будут заменены, и, следовательно, внешний вид текста в документе будет отражать новые стили.
string fromDoc = args[0];
string toDoc = args[1];
ReplaceStyles(fromDoc, toDoc);
Принципы работы кода
Код извлекает и заменяет часть стилей сначала, а затем часть stylesWithEffects второй и использует два вспомогательных метода для выполнения большей части работы. Метод ExtractStylesPart
выполняет задание извлечения содержимого части стилей или stylesWithEffects и помещения его в XDocument объект . Метод ReplaceStylesPart
принимает объект, созданный ExtractStylesPart
и использует его содержимое для замены части стилей или stylesWithEffects в целевом документе.
// Extract and replace the styles part.
XDocument? node = ExtractStylesPart(fromDoc, false);
if (node is not null)
{
ReplaceStylesPart(toDoc, node, false);
}
Последний параметр в сигнатуре для ExtractStylesPart
метода или ReplaceStylesPart
определяет, используется ли часть стилей или часть stylesWithEffects. Значение false указывает, что требуется извлечь и заменить часть стилей. Отсутствие значения (параметр необязательный) или значения true (значение по умолчанию) означает, что необходимо извлечь и заменить часть stylesWithEffects.
// Extract and replace the stylesWithEffects part. To fully support
// round-tripping from Word 2010 to Word 2007, you should
// replace this part, as well.
node = ExtractStylesPart(fromDoc);
if (node is not null)
{
ReplaceStylesPart(toDoc, node);
}
return;
Дополнительные сведения о методе ExtractStylesPart
см. в соответствующем примере. В следующем разделе описывается ReplaceStylesPart
метод .
Метод ReplaceStylesPart
Метод ReplaceStylesPart
можно использовать для замены стилей или части styleWithEffects в документе с учетом XDocument
экземпляра, содержащего ту же часть для документа Word 2010 или Word 2013 и более поздних версий (как показано в примере кода ранее в этом разделе, ExtractStylesPart
метод можно использовать для получения этого экземпляра). Метод ReplaceStylesPart
принимает три параметра: первый параметр содержит строку, указывающую путь к файлу, который требуется изменить. Второй параметр содержит объект, содержащий часть стилей XDocument
или стилейWithEffect из другого документа word, а третий указывает, требуется ли заменить часть стилей или часть stylesWithEffects (как показано в примере кода ранее в этом разделе, необходимо дважды вызвать эту процедуру для документов Word 2013 и более поздних версий, заменив каждую часть соответствующей частью из исходного документа).
static void ReplaceStylesPart(string fileName, XDocument newStyles, bool setStylesWithEffectsPart = true)
Принцип работы кода ReplaceStylesPart
Метод ReplaceStylesPart
проверяет указанный документ и ищет стили или стилиWithEffects.
Если запрошенная часть существует, метод сохраняет предоставленный XDocument
объект в выбранной части.
Код начинается с открытия документа с помощью Open метода и указания, что документ должен быть открыт для чтения и записи (последний true
параметр). При открытии документа код использует MainDocumentPart свойство для перехода к части документа main, а затем подготавливает переменную с именем stylesPart
для хранения ссылки на часть стилей.
// Open the document for write access and get a reference.
using (var document = WordprocessingDocument.Open(fileName, true))
{
if (document.MainDocumentPart is null || (document.MainDocumentPart.StyleDefinitionsPart is null && document.MainDocumentPart.StylesWithEffectsPart is null))
{
throw new ArgumentNullException("MainDocumentPart and/or one or both of the Styles parts is null.");
}
// Get a reference to the main document part.
var docPart = document.MainDocumentPart;
// Assign a reference to the appropriate part to the
// stylesPart variable.
StylesPart? stylesPart = null;
Поиск части "Правильные стили"
Затем код получает ссылку на запрошенную часть стилей с помощью setStylesWithEffectsPart
логического параметра. На основе этого значения код получает ссылку на запрошенную часть стилей и сохраняет ее в переменной stylesPart
.
if (setStylesWithEffectsPart)
{
stylesPart = docPart.StylesWithEffectsPart;
}
else
{
stylesPart = docPart.StyleDefinitionsPart;
}
Сохранение содержимого части
При условии, что запрошенная часть существует, код должен сохранить все содержимое объекта, XDocument
переданного методу в часть. Каждая часть предоставляет GetStream() метод , который возвращает Stream.
Код передает экземпляр Stream конструктору StreamWriter(Stream) класса , создавая модуль записи потока вокруг потока части. Наконец, код вызывает Save(Stream) метод XDocument, сохраняя его содержимое в части стилей.
// If the part exists, populate it with the new styles.
if (stylesPart is not null)
{
newStyles.Save(new StreamWriter(stylesPart.GetStream(FileMode.Create, FileAccess.Write)));
}
Пример кода
Ниже приведены полные ReplaceStyles
методы , ReplaceStylesPart
и ExtractStylesPart
в C# и Visual Basic.
// Replace the styles in the "to" document with the styles in
// the "from" document.
static void ReplaceStyles(string fromDoc, string toDoc)
{
// Extract and replace the styles part.
XDocument? node = ExtractStylesPart(fromDoc, false);
if (node is not null)
{
ReplaceStylesPart(toDoc, node, false);
}
// Extract and replace the stylesWithEffects part. To fully support
// round-tripping from Word 2010 to Word 2007, you should
// replace this part, as well.
node = ExtractStylesPart(fromDoc);
if (node is not null)
{
ReplaceStylesPart(toDoc, node);
}
return;
}
// Given a file and an XDocument instance that contains the content of
// a styles or stylesWithEffects part, replace the styles in the file
// with the styles in the XDocument.
static void ReplaceStylesPart(string fileName, XDocument newStyles, bool setStylesWithEffectsPart = true)
{
// Open the document for write access and get a reference.
using (var document = WordprocessingDocument.Open(fileName, true))
{
if (document.MainDocumentPart is null || (document.MainDocumentPart.StyleDefinitionsPart is null && document.MainDocumentPart.StylesWithEffectsPart is null))
{
throw new ArgumentNullException("MainDocumentPart and/or one or both of the Styles parts is null.");
}
// Get a reference to the main document part.
var docPart = document.MainDocumentPart;
// Assign a reference to the appropriate part to the
// stylesPart variable.
StylesPart? stylesPart = null;
if (setStylesWithEffectsPart)
{
stylesPart = docPart.StylesWithEffectsPart;
}
else
{
stylesPart = docPart.StyleDefinitionsPart;
}
// If the part exists, populate it with the new styles.
if (stylesPart is not null)
{
newStyles.Save(new StreamWriter(stylesPart.GetStream(FileMode.Create, FileAccess.Write)));
}
}
}
// Extract the styles or stylesWithEffects part from a
// word processing document as an XDocument instance.
static XDocument ExtractStylesPart(string fileName, bool getStylesWithEffectsPart = true)
{
// Declare a variable to hold the XDocument.
XDocument? styles = null;
// Open the document for read access and get a reference.
using (var document = WordprocessingDocument.Open(fileName, false))
{
// Get a reference to the main document part.
var docPart = document.MainDocumentPart;
if (docPart is null)
{
throw new ArgumentNullException("MainDocumentPart is null.");
}
// Assign a reference to the appropriate part to the
// stylesPart variable.
StylesPart stylesPart;
if (getStylesWithEffectsPart && docPart.StylesWithEffectsPart is not null)
{
stylesPart = docPart.StylesWithEffectsPart;
}
else if (docPart.StyleDefinitionsPart is not null)
{
stylesPart = docPart.StyleDefinitionsPart;
}
else
{
throw new ArgumentNullException("StyleWithEffectsPart and StyleDefinitionsPart are undefined");
}
using (var reader = XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
// Create the XDocument.
styles = XDocument.Load(reader);
}
}
// Return the XDocument instance.
return styles;
}