Показаны сообщения с ярлыком Open Office XML. Показать все сообщения
Показаны сообщения с ярлыком Open Office XML. Показать все сообщения

пятница, 10 января 2014 г.

Генерация произвольных файлов формата MS Office 2007/2010 (Word и Excel) в SharePoint 2010 и 2013

Нередко при тестировании и отладке решений для SharePoint нужно загрузить n-ое количество тестовых файлов в библиотеку документов.
Хорошо если наши запросы скромные, и для тестирования достаточно скопировать несколько файлов через "режим проводника" из локальной папки.
Но как быть если таких файлов нужно загрузить не одну тысячу в несколько библиотек?
Один из вариантов - воспользоваться Open XML SDK 2.0 for Microsoft Office.

Пример разработки приложения для генерации произвольных файлов формата MS Word и Excel в SharePoint 

В своей предыдущей статье я уже описывал как начать работу с Open XML SDK 2.0 for Microsoft Office и создать проект в Visual Studio 2010, поэтому повторяться не буду.
Начнем сразу с главного: как генерировать файлы формата MS Office "на лету".

Генерация файла формата MS Word 2007/2010

В данном примере я приведу метод, позволяющий генерировать файл MS Word, содержащий одну строку текста, c использованием Open XML SDK 2.0.
Листинг с комментариями приведен ниже.
public static void GenerateWordDocument(Stream doc, string documentContentText)
{
 //Создаем Wordprocessing документ 
 using (WordprocessingDocument myDoc = WordprocessingDocument.Create(doc, WordprocessingDocumentType.Document))
 {
  //Добавляем новую главную часть документа - main document part
  MainDocumentPart mainPart = myDoc.AddMainDocumentPart();
  mainPart.Document = new Document();
  //Создаем тело документа - Body (этот элемент содержит
  //другие элементы, которые мы хотим добавить в документ) 
  Body body = new Body();
  //Создаем параграф 
  Paragraph paragraph = new Paragraph();
  Run run_paragraph = new Run();
  //Размещаем текст в документе 
  Text text_paragraph = new Text(documentContentText);
  run_paragraph.Append(text_paragraph);
  paragraph.Append(run_paragraph);
  body.Append(paragraph);
  mainPart.Document.Append(body);
  //Сохраняем изменения в главной части документа 
  mainPart.Document.Save();
 }
}

Генерация файла формата MS Excel 2007/2010

Для генерации различных отчетов в виде файлов MS Excel я часто использую библиотеку EEPlus из проекта EPPlus-Create advanced Excel 2007 spreadsheets on the server. 
С помощью этой библиотеки метод, позволяющий сгенерировать Excel файл, содержащий ячейку с текстом, умещается в несколько строк (сравните для примера с описанием генерации c применением "чистого" Open XML SDK 2.0 на msdn). 
Листинг с комментариями приведен ниже.
public static void GenerateExcelDocument(Stream doc, string documentContentText)
{
 using (var xls = new ExcelPackage(doc))
 {
  //Добавляем лист в книгу Excel
  var worksheet = xls.Workbook.Worksheets.Add("Worksheets1");
  //Задаем ширину первого столбца
  worksheet.Column(1).Width = 30;
  //Указываем содержимое первой ячейки в первом столбце
  worksheet.Cells[1, 1].Value = documentContentText;
  //Добавляем метаданные к файлу
  xls.Workbook.Properties.Title = "Excel2010";
  xls.Workbook.Properties.Author = "UploadDocs2Lib";
  xls.Save();
 }
}
Если требуется вставить автофильтр в Excel файл, то нужно явно применить его ко всему листу: worksheet.Cells[worksheet.Dimension.Address].AutoFilter=true;

Написание приложения для генерации файлов

Все исходные коды приложения опубликованы на https://github.com/eof151/SPUploadDocs2Lib.

Создадим новое WinForms приложение, я назвал его  - SPUploadDocs2Lib.
Структура итогового проекта изображена на рисунке ниже. Не забываем добавить референсы на библиотеки DocumentFormat.OpenXml и EEPlus.
Рисунок 1.
Два вышеприведенных метода я объединил в файле с одноименным классом DocumentBuilder.cs
Для генерации файлов определенного типа уже непосредственно в определенной библиотеке документов SharePoint  я написал общий метод, код которого приведен в листинге ниже.
private void GenerateFile(SPList myList, bool isWord)
{
 SPListItem item = null;
 string documentName = "";
 MemoryStream docStream = new MemoryStream();
 StreamWriter writer = new StreamWriter(docStream);

 if (isWord) //Word
 {
  //Генерируем случайное имя файла, используя префикс и соответствующее расширение
  documentName = GeneratePartFileName(tbFilenamePref.Text, WORDFILE);
  DocumentBuilder.GenerateWordDocument(docStream, tbWordText.Text);
 }
 else //Excel
 {
  documentName = GeneratePartFileName(tbExcFilenamePref.Text, EXCELFILE);
  DocumentBuilder.GenerateExcelDocument(docStream, tbExcelText.Text);
 }

 SPFile file = myList.RootFolder.Files.Add(documentName, docStream, true);
 writer.Flush();
 item = file.Item;
 //Заполняем свойство - Название
 item["Title"] = "TestFile" + item.ID;
 item.Update();
}
Вид окна программы приведен на рисунке ниже.

Рисунок 2.

Результат работы программы

После запуска в окне программы необходимо указать URL-адрес сайта и название библиотеки документов, в которой планируется сгенерировать файлы. После этого остается указать число создаваемых файлов их префиксы и текст содержимого. 
Нажимаем на соответствующие кнопки с иконками MS Word или MS Excel и смотрим на результат:

Рисунок 3. Результат работы программы в SharePoint 2010


Рисунок 4. Результат работы программы в SharePoint 2013

Пример содержимого в сгенерированных файлах MS Office приведен на рисунках ниже.

Рисунок 5. Содержимое сгенерированного файла MS Word


Рисунок 6. Содержимое сгенерированного файла MS Excel

Подведем итоги

Как вы успели заметить, посредством небольшого количества кода и Open XML SDK 2.0 for Microsoft Office можно просто генерировать множество файлов MS Office в библиотеках документов SharePoint.

Полезные ссылки

  1. Creating Documents by Using the Open XML Format SDK Version 2.0 CTP (Part 1 of 3)
  2. Creating Documents by Using the Open XML Format SDK Version 2.0 CTP (Part 2 of 3)
  3. Creating Documents by Using the Open XML Format SDK Version 2.0 CTP (Part 3 of 3)
  4. How to: Create a word processing document by providing a file name (Open XML SDK)
  5. Creation of a Word 2007 document using the Open XML Format SDK

воскресенье, 1 сентября 2013 г.

Добавление изображения в документ Word через OpenXML

Разве это не очевидно? Чем мы всё чаще сталкиваемся с задачами, связанными с автоматизацией создания и редактирования "ms officных" документов, тем нам всё чаще приходит на ум название: Open XML SDK 2.0 for Microsoft Office. Сегодня речь пойдет об автоматизации вставки изображения в определенную область в документе формата MS Word (.docx).

Пример разработки приложения для вставки изображения в документ MS Word 

Определение области в документе для вставки изображения

Для того чтобы задать определенное место в документе, в которое будет осуществлена вставка изображения мы воспользуемся функционалом из вкладки "Разработчик". 

Если у вас данная вкладка не отображается, то включить ее можно в "Параметры Word" -> "Настройка ленты".

Далее в самом документе ставим курсор в необходимое место (в котором необходимо разместить рисунок), нажимаем на панели "Разработчик" кнопку: Элемент управления содержимым 'рисунок', как показано на примере ниже.

В результате в документе появится редактируемая область, размеры которой можно изменять. Данную область можно сразу заполнить рисунком, выбрав файл изображения из локальной папки. Но для наших целей мы оставим её пустой, немного изменив размер, используя маркеры.

Нас особо интересует возможность редактирования свойств данной области - "Название" и "Тег". Для этого выделите область и нажмите "Свойства" на панели "Разработчик". В поле "Название" мы укажем произвольное имя данной области, например - "Здесь будет картинка!". В поле "Тег" впишем особую метку для данной области, которую будем использовать далее, например - "рисунок".

Нажимаем "ОК" на панели свойств. Сохраняем документ. В итоге документ принимает вид как на рисунке ниже. 

Подготовка закончена!

Начало работы с Open XML SDK 2.0 for Microsoft Office

Для старта нам необходимо скачать и установить инсталляционный пакет Open XML SDK 2.0 for Microsoft Office из Download Center. После инсталляции пакета в папке установки Open XML SDK по пути Open XML SDK\V2.0\lib можно найти библиотеку DocumentFormat.OpenXml.dll, которой мы и воспользуемся.

Подготовка проекта в Visual Studio 2010

В рамках данной статьи я продемонстрирую, на примере создания Windows Forms приложения на языке C#. Для начала необходимо создать в Visual Studio 2010 новый проект типа "Windows Forms Application". Я назвал проект "WordInsertPicture".

Далее в проекте нужно добавить референс на библиотеку DocumentFormat.OpenXml.dll, которую для удобства можно скопировать в папку с проектом.

WordprocessingDocument

Для открытия документа Word из потока используем класс WordprocessingDocument, следующим образом:
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(myStream, false))
{
  foreach (var item in wordDoc.MainDocumentPart.Document.Body)
  {
      var oo = item.Descendants<SdtProperties>();
      foreach (var f1 in oo)
      {
          _contName = FindPictureContainer(wordDoc, f1, ref _sdtPropId);
      }
  }
}
В цикле foreach перебираем все элементы в теле документа Body. Далее для каждого элемента проверяем наличие тега <w:sdtPr>  и обозначающего: Structured Document Tag Properties. В WordprocessingDocument этот тег десериализуется в класс SdtProperties.  Этот элемент определяет набор свойств, которые должны применяться к родительскому тегу документа. Родительским тегом в нашем случае является тег <w:sdt>, в котором и содержится искомая область для вставки рисунка <w:drawing>.

 
  
  
  
  
  
 
 
  
   
    
   
   
    
     
     
    
    
     
      
      
      
      
       
      
      
       
        
         
          
          
           
          
         
         
          
           
            
             
            
           
          
          
          
           
          
         
         
          
           
           
          
          
           
          
          
          
           
          
         
        
       
      
     
    
   
  
 



Поиск области для вставки

Для поиска нужной области для вставки изображения воспользуемся свойством "Тег", значение которого мы определили как "рисунок" в предыдущем пункте. В OpenXML данному свойству соответствует тег <w:tag>, который десериализуется в класс Tag.
Также нас интересуют теги:

  • <w:alias>  он же SdtAlias, название области, которое мы задали в самом документе
  • <w:id> он же SdtId, ID области, является уникальным в рамках одного документа

В листинге, приведенном ниже, я описал метод, в котором осуществляется проверка соответствия тега в области значению - "рисунок", после чего возвращаются значения alias и id.
private string FindPictureContainer(WordprocessingDocument wdDoc, OpenXmlElement uy, ref string SdtId)
{
    SdtAlias alias = uy.Elements<SdtAlias>().FirstOrDefault();
    SdtId sdtId = uy.Elements<SdtId>().FirstOrDefault();
    Tag tag = uy.Elements<Tag>().FirstOrDefault();

    string _tag = "";
    string _sdtId = "";
    string _alias = "";

    //Получаем тег контейнера
    if (tag != null)
        _tag = tag.Val;

    //Получаем ID контейнера
    if (sdtId != null)
        SdtId = _sdtId = sdtId.Val;

    //Получаем название контейнера
    if (alias != null)
        _alias = alias.Val;

    if (_tag.Contains("рисунок"))
    {
        try
        {
            var sdtBlock = wdDoc.MainDocumentPart.Document.Descendants<SdtBlock>()
                        .Where(r => r.SdtProperties.GetFirstChild<SdtId>().Val == _sdtId);
            if (sdtBlock != null)
            {
                Drawing dr = sdtBlock.First().Descendants<Drawing>().FirstOrDefault();
                if (dr != null)
                {
                    GetSizeOfPictureContainer(dr, ref _contWidth, ref _contHeight);
                }
            }
        }
        catch (Exception ex)
        {
            ErrorHandler(ex);
        }
    }
    return _alias;
}

Описание метода GetSizeOfPictureContainer() привожу в листинге ниже. Этот метод определяет размеры области в документе для дальнейшего пропорционального масштабирования изображения при вставке.
private void GetSizeOfPictureContainer(Drawing d, ref int maxWidth, ref int maxHeight)
{
    Extent imageSizeProps = d.Descendants<Extent>().FirstOrDefault();
    maxWidth = (int)(imageSizeProps.Cx / 9525);
    maxHeight = (int)(imageSizeProps.Cy / 9525);
}

Пропорциональное изменение размеров изображения

Так как вставляемое изображение может быть больше или меньше области для вставки, то необходимо вычислить новые размеры для соблюдения пропорций.
В листинге ниже я привожу пример соответствующего метода:
private static void ResizePictureContainer(Drawing d, int originalWidth, int originalHeight, ref int maxWidth, ref int maxHeight)
{
    Extent imageSizeProps = d.Descendants<Extent>().FirstOrDefault();

    if (imageSizeProps != null)
    {
        int imageWidthOr = (int)(imageSizeProps.Cx / 9525);
        int imageHeightOr = (int)(imageSizeProps.Cy / 9525);
        maxWidth = imageWidthOr;
        maxHeight = imageHeightOr;
        //Определяем соотношение сторон
        double aspectRatio = (double)originalWidth / (double)originalHeight;

        //Проверяем, что высота изображения больше разрешенной высоты
        int newHeight = (originalHeight > imageHeightOr) ? imageHeightOr : originalHeight;
        //Проверяем, что ширина изображения больше разрешенной ширины
        int newWidth = (originalWidth > imageWidthOr) ? imageWidthOr : originalWidth;
        //Вычисляем новую высоту или ширину в зависимости от соотношения сторон (полагаясь на ориентацию изображения)
        if ((newWidth == originalWidth) && (newHeight == originalHeight))
        {
            //Если ширина больше, то ориентация книжная
            if (newWidth > newHeight)
            {
                //Вычисляем новую высоту умножением ширины на соотношение сторон
                newHeight = (int)(imageWidthOr / aspectRatio);
                newWidth = imageWidthOr;
                //в некторых случаях вычисленная высота может быть больше чем разрешенная 
                //поэтому нужно подвести высоту к разрешенной и пересчитать ширину
                if (newHeight > imageHeightOr)
                {
                    newHeight = imageHeightOr;
                    newWidth = (int)(aspectRatio * newHeight);
                }
            }
            else //ориентация портретная
            {
                //Вычисляем новую ширину умножением высоты на соотношение сторон
                newWidth = (int)(aspectRatio * imageHeightOr);
                newHeight = imageHeightOr;
            }
        }
        else //Если исходное изображение меньше, чем контейнер
        {
            if (newWidth > newHeight)
            {
                newHeight = (int)(newWidth / aspectRatio);
                if (newHeight > imageHeightOr)
                {
                    newHeight = imageHeightOr;
                    newWidth = (int)(aspectRatio * newHeight);
                }
            }
            else 
            {
                newWidth = (int)(aspectRatio * newHeight);
            }
        }
        imageSizeProps.Cx = (long)(newWidth * 9525);
        imageSizeProps.Cy = (long)(newHeight * 9525);
    }

    Extents e2 = d.Descendants<Extents>().FirstOrDefault();

    long imageWidthEmu = (long)(originalWidth * 9525);
    long imageHeightEmu = (long)(originalHeight * 9525);
    if (e2 != null)
    {
        e2.Cx = imageWidthEmu;
        e2.Cy = imageHeightEmu;
    }
}

Вставка изображения

Для вставки изображения нужно обратить внимание на два метода:

  • wordprocessingDocument.MainDocumentPart.AddImagePart()
  • ImagePart.FeedData()
Листинг с примером приведен ниже:
ImagePart ip = wordprocessingDocument.MainDocumentPart.AddImagePart(ImagePartType.Jpeg);
using (Stream inStream = openFileDialog2.OpenFile())
{            
 inStream.Position = 0;
 ip.FeedData(inStream);
}

Всеь проект для Visual Studio 2010 можно скачать по ссылке.

Результат работы программы

После запуска программы выбираем подготовленный документ MS Word из локальной папки, выбираем изображение для вставки и сохраняем результат.

Открываем документ и проверяем изображение.

Подведем итоги

Вставкой изображений не исчерпываются возможности работы с документами MS Word через OpenXML. О других применениях мы поговорим в других статьях!