gifts2017

Когда шаблоны рвутся, или Вывод в Word по-простому

Опубликовал Яков Коган (Yashazz) в раздел Программирование - Практика программирования

Множество известных сейчас механизмов, в т.ч. интегрированных в типовые конфигурации, используют концепцию подготовки вордовского шаблона (dot) и затем его курочат простой заменой, подстановкой нужных значений в нужные места. Гораздо реже встречается вывод "с чистого листа" - построение динамически, предельно простым образом.

Поскольку шаблон далеко не всегда позволяет динамически построить нужный конечный документ, будем делать его сами поэтапно, а для этого нам понадобится инструментарий. Напомню, речь о совершенно обычном com-соединении с приложением MS Word, не ниже 2007 (ниже не проверял), установленном на клиентском ПК.

И, т.к. 90% задач сводятся к довольно простым и обыденным действиям, в т.ч. по форматированию, предлагаю набор процедур, реализующих вывод в документ Word нужных данных с минимальными возможностями оформления. Разумеется, знающий VBA посоветует массу других решений, особенно в части весьма "жадного" вывода данных в ячейки таблицы, а пристальное изучение Object Browser'а поможет узнать массу интересного, но за основу можно принять уже и такое. Кстати, далеко не все действия можно записать как макросы и увидеть потом их "потроха", кое-что придётся искать и узнавать на просторах интернета.

К недостаткам следует отнести невозможность форматировать атомарный фрагмент текста - просто не дошли руки. Желающие, опираясь на эту концепцию, думаю, легко смогут доработать предлагаемую механику для себя.

Делалось всё на 8.3.6.2014 - но, думаю, на более ранних тоже заработает. Все действия, естественно, только на клиенте.

Собственно процедуры: 

#Область ВыводДанныхВДокументWord

#Область Вспомогательные

// Исходя из данных переданного аргумента, готовит и возвращает текстовую строку,
// пригодную для вывода в область документа.
// При ошибке возвращает пустую строку.
//
// Параметры:
//    исхТекст - строка, форматированная строка или форматированный документ (в случае
//       форматированного документа производится разбор по параграфам с выделением текстовых
//       фрагментов и их соединением через символы переносов строк.
//
&НаКлиенте
Функция ПодготовитьТекстовыйФрагмент(исхТекст)
Попытка
    рТип=ТипЗнч(исхТекст);
    Если рТип=Тип("Строка") Тогда
        рТекст=исхТекст;
    ИначеЕсли рТип=Тип("ФорматированнаяСтрока") Тогда
        рТекст=Строка(исхТекст);
    ИначеЕсли рТип=Тип("ФорматированныйДокумент") Тогда
        рТекст=""; разд="";
        Для каждого рПараграф Из исхТекст.Элементы Цикл
            Для каждого эл Из рПараграф.Элементы Цикл
                Если ТипЗнч(эл)=Тип("ТекстФорматированногоДокумента") Тогда
                    рТекст=рТекст+разд+эл.Текст; разд=Символы.ПС;
                КонецЕсли;
            КонецЦикла;
        КонецЦикла;
        Пока Истина Цикл
            ОбработкаПрерыванияПользователя();
            рТекст=СтрЗаменить(рТекст,"  "," ");
            Если Найти(рТекст,"  ")=0 Тогда Прервать КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    Возврат рТекст;
Исключение
    Сообщить("ПодготовитьТекстовыйФрагмент, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
    Возврат "";
КонецПопытки;
КонецФункции

// Находит первый (!) из подходящих под условия поиска диапазонов документа и возвращает его;
// При ошибке, или если ничего не найдено, возвращает Неопределено.
//
// Параметры:
//    комДокумент - объект Document в COM-объекте Word;
//    комДиапазон - объект Range в COM-объекте Word (необязательный, если не указан, поиск идёт во всём документе);
//    Поискуха - строка, содержимое которых собственно разыскивается.
//
&НаКлиенте
Функция НайтиПодстроку(комДокумент,комДиапазон=Неопределено,Поискуха)
Попытка
    Если комДиапазон=Неопределено Тогда комДиапазон=комДокумент.Range() КонецЕсли; // по умолчанию весь документ
    комПоиск=комДиапазон.Find;
    комПоиск.ClearFormatting();
    комПоиск.Text=Поискуха;
    комПоиск.Forward=Истина;
    комПоиск.Wrap=1; //wdFindContinue
    комПоиск.Format=Ложь;
    комПоиск.MatchCase=Ложь;
    комПоиск.MatchWholeWord=Ложь;
    комПоиск.MatchAllWordForms=Ложь;
    комПоиск.MatchSoundsLike=Ложь;
    комПоиск.MatchWildcards=Истина;
    Если комПоиск.Execute() Тогда
        Возврат комДокумент.Range(комДиапазон.Start,комДиапазон.End);
    Иначе
        Возврат Неопределено;
    КонецЕсли;
Исключение
    Сообщить("НайтиПодстроку, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
    Возврат Неопределено;
КонецПопытки;
КонецФункции

// Выводит в COM-объект КомВорд разделитель в виде нового параграфа
&НаКлиенте
Процедура ВывестиРазделитель(КомВорд)
    // выход в область дальнейшего вывода
    КомВорд.Selection.EndKey(6);
    КомВорд.Selection.TypeParagraph();
КонецПроцедуры

// Выводит в COM-объект КомВорд разделитель в виде разрыва страницы
&НаКлиенте
Процедура ВставитьРазрывСтраницы(КомВорд)
    КомВорд.Selection.EndKey(6);
    КомВорд.Selection.InsertBreak(7); // wdPageBreak
КонецПроцедуры

// Устанавливает для шрифта документа переданные свойства из структуры
//
// Параметры
//    комШрифт - объект Font в COM-объекте Word;
//    рШрифт - объект "Шрифт" или структура, обязательно содержащая следующие значения:
//        Имя - строковое имя, совпадающее с именами согласно нотации Word,
//        Размер - реальное числовое значение или "-1", если надо установить по умолчанию;
//        Жирный, Наклонный, Подчеркивание (булево) - соответственно.
//
&НаКлиенте
Процедура УстановитьСвойстваШрифта(комШрифт,рШрифт);
Попытка
    wdToggle=9999998;
    wdColorAutomatic=-16777216;
    комШрифт.Name=рШрифт.Имя;
    Если рШрифт.Размер=-1 Тогда // по умолчанию
        комШрифт.Size=12;
    Иначе
        комШрифт.Size=рШрифт.Размер;
    КонецЕсли;
    Если рШрифт.Жирный=Истина Тогда
        комШрифт.Bold=wdToggle;
    КонецЕсли;
    Если рШрифт.Наклонный=Истина Тогда
        комШрифт.Italic=wdToggle;
    КонецЕсли;
    Если рШрифт.Подчеркивание=Истина Тогда
        комШрифт.UnderlineColor=wdColorAutomatic;
        комШрифт.Underline=1; // wdUnderlineSingle
    КонецЕсли;

    #Область ПрочиеСвойствШрифта
    //комШрифт.StrikeThrough=wdUndefined
    //комШрифт.Subscript=wdUndefined
    //комШрифт.Superscript=wdUndefined
    //комШрифт.Shadow=wdUndefined
    //комШрифт.Outline=wdUndefined
    //комШрифт.Emboss=wdUndefined
    //комШрифт.Engrave=wdUndefined
    //комШрифт.AllCaps=wdUndefined
    //комШрифт.Hidden=wdUndefined
    //комШрифт.Color=wdUndefined
    //комШрифт.Animation=wdUndefined
    //комШрифт.DoubleStrikeThrough=wdUndefined
    #КонецОбласти

Исключение
    Сообщить("УстановитьСвойстваШрифта, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
КонецПопытки;
КонецПроцедуры

// Устанавливает всем элементам коллекции Borders одинаковое значение
//
// Параметры:
//    комОбъект - любой объект, имеющий свойство Borders, в COM-объекте Word;
//    рСтиль - числовое значение согласно нотации wdLineStyle.
//
&НаКлиенте
Процедура УстановитьОдинаковыеГраницы(комОбъект,рСтиль=0)
    Для й=1 По 8 Цикл // см. значения констант wdBorderType
        Попытка // не все номера в этом семействе существуют всегда
            комОбъект.Borders(-1*й).LineStyle=рСтиль; // см. wdLineStyle
        Исключение
        КонецПопытки;
    КонецЦикла;
КонецПроцедуры

// Устанавливает соответствие между константами Word и значениями 1С; не использована
// в текущем коде и приведена просто как пример работы с гориз.выравниваниями.
//
Функция ПолучитьВыравнивание();
    соот=Новый Соответствие;
    соот.Вставить(ПредопределенноеЗначение("ГоризонтальноеПоложение.Лево"),0);
    соот.Вставить(ПредопределенноеЗначение("ГоризонтальноеПоложение.Центр"),1);
    соот.Вставить(ПредопределенноеЗначение("ГоризонтальноеПоложение.Право"),2);
    соот.Вставить(ПредопределенноеЗначение("ГоризонтальноеПоложение.ПоШирине"),3);
    Возврат соот;
КонецФункции

#КонецОбласти


#Область ПримерВызова_ДобавитьНумерованныйСписок
//рШрифтПоУмолчанию=Новый Шрифт("Times New Roman",12); // по умолчанию
//
//мспис=Новый Массив;
//мспис.Добавить(Новый Структура("Номер,Текст",1,"Первый"));
//мспис.Добавить(Новый Структура("Номер,Текст",2,"Второй"));
//мспис.Добавить(Новый Структура("Номер,Текст,Шрифт",3,"Жирный",Новый Шрифт(,,Истина)));
//мспис.Добавить(Новый Структура("Номер,Текст",4,"Четвёртый"));
//
//пар=Новый Структура;
//пар.Вставить("Содержание",мспис);
//пар.Вставить("КомВорд",КомВорд);
//пар.Вставить("КомДокумент",d);
//пар.Вставить("ДиапазонПолучатель",КомВорд.Selection.Range);
//пар.Вставить("ВыравниваниеНомеров",wdAlignParagraphRight);
//пар.Вставить("ВыравниваниеТекста",wdAlignParagraphLeft);
//пар.Вставить("ШиринаНомеров",45);
//пар.Вставить("ШиринаТекста",370);
//пар.Вставить("ШрифтПоУмолчанию",рШрифтПоУмолчанию);
//
//ДобавитьНумерованныйСписок(пар);
//
#КонецОбласти
//
// Выводит не настоящий маркированный список с номерами, а его эмуляцию в виде двухколоночной таблицы
//
// Параметры:
//    рПараметры - структура,
//    обязательно содержащая ключи:
//        КомВорд - собственно COM-объект Word.Application;
//        КомДокумент - объект Document в COM-объекте Word;
//        ДиапазонПолучатель - объект Range в COM-объекте Word;
//        Содержание - данные формы или массив структур, т.е. коллекция, имеющая следующий внутренний вид:
//            Номер - порядковый номер как строка (если не указан, то по возрастанию, начиная с 1);
//            Текст - содержимое пункта, строка;
//            Шрифт - объект Шрифт, уточняющий вывод конкретной позиции;
//    необязательно содержащая ключи:
//        ВыравниваниеНомеров - см. ПолучитьВыравнивание, касается возрастающих №№ выводимого списка;
//        ВыравниваниеТекста - см. ПолучитьВыравнивание, касается содержимого собственно пунктов списка;
//        ШиринаНомеров - ширина колонки, где выводятся №№ (рекомендуется делать небольшой);
//        ШиринаТекста - ширина колонки, где выводится содержание пунктов списка;
//        ШрифтПоУмолчанию - объект Шрифт, которым будет выведено содержимое, если нет уточнений в Содержании.
//
&НаКлиенте
Процедура ДобавитьНумерованныйСписок(рПараметры)
Попытка
    рСписок=рПараметры.Содержание; // таблица значений или массив структур
    комДокумент=рПараметры.КомДокумент;
    квоКолонок=2; // для № и для текста
    комТаблица=комДокумент.Tables.Add(рПараметры.ДиапазонПолучатель,рСписок.Количество(),квоКолонок); // изначально без рамок
    //
    рВыравниваниеНомеров=?(рПараметры.Свойство("ВыравниваниеНомеров"),рПараметры.ВыравниваниеНомеров,0);
    рВыравниваниеТекста=?(рПараметры.Свойство("ВыравниваниеТекста"),рПараметры.ВыравниваниеТекста,3);
    рШиринаНомеров=?(рПараметры.Свойство("ШиринаНомеров"),рПараметры.ШиринаНомеров,45);
    рШиринаТекста=?(рПараметры.Свойство("ШиринаТекста"),рПараметры.ШиринаТекста,370);
    рШрифтПоУмолчанию=?(рПараметры.Свойство("ШрифтПоУмолчанию"),рПараметры.ШрифтПоУмолчанию,Неопределено);
    Если ТипЗнч(рШрифтПоУмолчанию)<>Тип("Шрифт") Тогда
        рШрифтПоУмолчанию=Новый Шрифт("Times New Roman",12); // -1, если по умолчанию как в общем стиле
    КонецЕсли;
    //
    Попытка
        комТаблица.Columns(1).SetWidth(рШиринаНомеров,0);
        комТаблица.Columns(2).SetWidth(рШиринаТекста,0);
    Исключение
    КонецПопытки;
    //
    Для й=1 По рСписок.Количество() Цикл
        строспис=рСписок.Получить(й-1);
        рНомер=?(строспис.Свойство("Номер"),строспис.Номер,Строка(й));
        комТаблица.Cell(й,1).Range.Text=СокрЛП(рНомер);
        комТаблица.Cell(й,2).Range.Text=строспис.Текст;
        комТаблица.Cell(й,1).Range.ParagraphFormat.Alignment=рВыравниваниеНомеров;
        комТаблица.Cell(й,2).Range.ParagraphFormat.Alignment=рВыравниваниеТекста;
        рШрифт=?(строспис.Свойство("Шрифт"),строспис.Шрифт,рШрифтПоУмолчанию);
        комШрифт=комТаблица.Rows(й).Range.Font;
        УстановитьСвойстваШрифта(комШрифт,рШрифт);
    КонецЦикла;
    //
    УстановитьОдинаковыеГраницы(комТаблица);
Исключение
    Сообщить("ДобавитьНумерованныйСписок, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
КонецПопытки;
КонецПроцедуры


#Область ПримерВызова_ДобавитьМаркированныйСписок
//мспис=Новый Массив;
//мспис.Добавить(Новый Структура("Текст","Первая строка"));
//мспис.Добавить(Новый Структура("Текст","Второй пункт"));
//мспис.Добавить(Новый Структура("Текст","Третья позиция"));
//пар=Новый Структура;
//пар.Вставить("Содержание",мспис);
//пар.Вставить("КомВорд",КомВорд);
//пар.Вставить("КомДокумент",d);
//пар.Вставить("ДиапазонПолучатель",КомВорд.Selection.Range);
//пар.Вставить("ШрифтПоУмолчанию",рШрифт);
//ДобавитьМаркированныйСписок(пар);
//
#КонецОбласти
//
// Выводит классический маркированный список Word
//
// Параметры:
//    рПараметры - структура,
//    обязательно содержащая ключи:
//        КомВорд - собственно COM-объект Word.Application;
//        КомДокумент - объект Document в COM-объекте Word;
//        ДиапазонПолучатель - объект Range в COM-объекте Word;
//        Содержание - таблица значений или массив структур, т.е. коллекция, имеющая следующий внутренний вид:
//            Текст - содержимое пункта, строка.
//    необязательно содержащая ключи:
//        ШрифтПоУмолчанию - объект Шрифт, которым будет выведено содержимое безальтернативно на весь список.
//
&НаКлиенте
Процедура ДобавитьМаркированныйСписок(рПараметры)
Попытка
    рСписок=рПараметры.Содержание; // массив структур
    КомВорд=рПараметры.КомВорд;
    рШрифтПоУмолчанию=?(рПараметры.Свойство("ШрифтПоУмолчанию"),рПараметры.ШрифтПоУмолчанию,Неопределено);
    Если ТипЗнч(рШрифтПоУмолчанию)<>Тип("Шрифт") Тогда
        рШрифтПоУмолчанию=Новый Шрифт("Times New Roman",12); // -1, если по умолчанию как в общем стиле
    КонецЕсли;

    комДиапазон=рПараметры.ДиапазонПолучатель;
    комУровниЛиста=КомВорд.ListGalleries(1).ListTemplates(1).ListLevels(1);
    //комУровниЛиста.NumberFormat=ChrW(61623)
    //комУровниЛиста.NumberFormat="'"+"%1.";
    комУровниЛиста.TrailingCharacter=0;
    комУровниЛиста.NumberStyle=23;
    комУровниЛиста.NumberPosition=КомВорд.CentimetersToPoints(0.63);
    комУровниЛиста.Alignment=0;
    комУровниЛиста.TextPosition=КомВорд.CentimetersToPoints(1.27);
    //комУровниЛиста.TabPosition=wdUndefined
    комУровниЛиста.ResetOnHigher=0;
    комУровниЛиста.StartAt=1;
    комУровниЛиста.LinkedStyle="";

    УстановитьСвойстваШрифта(комУровниЛиста.Font,рШрифтПоУмолчанию); // шрифт пока без вариантов

    КомВорд.ListGalleries(1).ListTemplates(1).Name="";
    // применяем к диапазону
    комДиапазон.ListFormat.ApplyListTemplateWithLevel(КомВорд.ListGalleries(1).ListTemplates(1),False,0,2);
    комДиапазон.Select();
    комворд.Keyboard(1049);
    //
    // собственно вносим
    квоспис=рСписок.Количество();
    Для й=0 По квоспис-1 Цикл
        знч=рСписок.Получить(й);
        КомВорд.Selection.TypeText(СокрЛП(знч.Текст));
        КомВорд.Selection.TypeParagraph();
    КонецЦикла;
    //
    КомВорд.Selection.Range.ListFormat.RemoveNumbers(1); //wdNumberParagraph

    ВывестиРазделитель(КомВорд);
Исключение
    Сообщить("ДобавитьМаркированныйСписок, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
КонецПопытки;
КонецПроцедуры


#Область ПримерВызова_ДобавитьТекст
//рШрифтПоУмолчанию=Новый Шрифт("Times New Roman",12); // по умолчанию
//рРазмерОтступа=КомВорд.CentimetersToPoints(1.00);
//
//зак1=Новый Структура("Имя","z1");
//гип1=Новый Структура("ИмяЗакладки,Текст","z1","парня скромного и простого");
//мспис=Новый Массив;
//мспис.Добавить(Новый Структура("Текст,Закладка","Флит-лейтенант Бокасса задумчиво хмыкнул. Будучи человеком образованным (все же, бакалавр Университета Антильских островов по экономической социологии) он, конечно, помнил фильм «День сурка». И было сходство сценария год и неделю назад на Таити с сегодняшним сценарием в Порт-Морсби. Авиа-перелет, погрузка на паром, скрытное перемещение к целевому берегу, танковый десант, и штурм города. Но, если смотреть немного глубже, то сегодня все по-другому.",зак1));
//мспис.Добавить(Новый Структура("Текст","Противник - не французские легионеры, а невнятная армия Республики Папуа, которая состоит примерно из 3000, как бы, солдат, обученных, как попало, и вооруженных, чем попало. И вообще, они не противник. Ведь коммодор Гремлин уже связался с главным дежурным офицером гарнизона Порт-Морсби, и разъяснил, что не следует папуасским солдатам лезть не в свое дело."));
//мспис.Добавить(Новый Структура("Текст","Посольство Великобритании в Папуа (Высокая Комиссия Соединенного Королевства) расположено в комплексе, построенном в 5 км от берега, около площади Коука. На  площадь выходят несколько отелей и офисных зданий, включая знаменитую башню «Somare Foundation», известную как «Папуасская Падающая башня» (она построена с дефектами, так что не используется из-за высокого риска обрушения). А вообще, это элитный район, патрулируемый дисциплинированным"));
//мспис.Добавить(Новый Структура("Текст","меньшинством полиции. Здесь безопасно и тихо. Точнее, обычно здесь безопасно и тихо, но не в эту ночь. Персонал посольства уже начал догадываться, что эта ночь – особенная. Стрельба на улицах не редкость в ночном Порт-Морсби, но сейчас стрельбы было многовато, и еще внезапно пропала сотовая связь. В общем, что-то было не так, и старший офицер безопасности посольства приказал перевести охрану на усиленный режим несения службы,"));
//мспис.Добавить(Новый Структура("Текст,Гиперссылка","и это случилось из-за одного человека, парня скромного и простого до невозможности.",гип1));
//
//пар=Новый Структура;
//пар.Вставить("Содержание",мспис);
//пар.Вставить("КомДокумент",d);
//пар.Вставить("КомВорд",КомВорд);
//пар.Вставить("ДиапазонПолучатель",КомВорд.Selection.Range);
//пар.Вставить("ВыравниваниеТекста",wdAlignParagraphJustify);
//пар.Вставить("ШиринаТекста",410);
//пар.Вставить("ШрифтПоУмолчанию",рШрифтПоУмолчанию);
//пар.Вставить("ОтступПоУмолчанию",рРазмерОтступа);
//
//ДобавитьТекст(пар);
//
#КонецОбласти
//
// Выводит текстовые фрагменты из некой коллекции в единственную колонку невидимой таблицы
//
// Параметры:
//    рПараметры - структура,
//    обязательно содержащая ключи:
//        КомВорд - собственно COM-объект Word.Application;
//        КомДокумент - объект Document в COM-объекте Word;
//        ДиапазонПолучатель - объект Range в COM-объекте Word;
//        Содержание - коллекция формы или массив структур, т.е. коллекция, имеющая следующий внутренний вид:
//            Текст - содержимое выводимого текстового фрагмента (внутреннее форматирование не предусмотрено);
//            Выравнивание - см. ПолучитьВыравнивание, для конкретного фрагмента;
//            Отступ - числовое значение отступа абзаца для конкретного фрагмента;
//            Шрифт - объект Шрифт, уточняющий шрифт фрагмента;
//            Заголовок - строка вида "Заголовок 1", "Заголовок 3" итд, определяющая структурный уровень фрагмента;
//            Закладка - структура с ключом "Имя", содержащим правильное имя закладки, вставляемой в начало фрагмента;
//            Гиперссылка - структура с ключами "ИмяЗакладки" (должна быть такая закладка) и "Текст" (гиперссылки);
//    необязательно содержащая ключи:
//        ВыравниваниеТекста - см. ПолучитьВыравнивание, которое будет установлено по умолчанию (если нет уточнений);
//        ШиринаТекста - ширина единственной колонки, где выводится собственно содержимое как текст;
//        ШрифтПоУмолчанию - объект Шрифт, которым будет выведено содержимое (если нет уточнений);
//        ОтступПоУмолчанию - числовое значение отступа абзаца для содержимого (если нет уточнений).
//
&НаКлиенте
Процедура ДобавитьТекст(рПараметры)
Попытка
    рСписок=рПараметры.Содержание;
    комДокумент=рПараметры.КомДокумент;
    квоКолонок=1; // для текста
    комТаблица=комДокумент.Tables.Add(рПараметры.ДиапазонПолучатель,рСписок.Количество(),квоКолонок);
    //
    рВыравниваниеПоУмолчанию=?(рПараметры.Свойство("ВыравниваниеТекста"),рПараметры.ВыравниваниеТекста,3);
    рШиринаТекста=?(рПараметры.Свойство("ШиринаТекста"),рПараметры.ШиринаТекста,410);
    рШрифтПоУмолчанию=?(рПараметры.Свойство("ШрифтПоУмолчанию"),рПараметры.ШрифтПоУмолчанию,Неопределено);
    Если ТипЗнч(рШрифтПоУмолчанию)<>Тип("Шрифт") Тогда
        рШрифтПоУмолчанию=Новый Шрифт("Times New Roman",12); // -1, если по умолчанию как в общем стиле
    КонецЕсли;
    рОтступПоУмолчанию=?(рПараметры.Свойство("ОтступПоУмолчанию"),рПараметры.ОтступПоУмолчанию,Неопределено);
    Если ТипЗнч(рОтступПоУмолчанию)<>Тип("Число") Тогда
        рОтступПоУмолчанию=-1;
    КонецЕсли;
    //
    Попытка
        комТаблица.Columns(1).SetWidth(рШиринаТекста,0);
    Исключение
    КонецПопытки;
    //
    Для й=1 По рСписок.Количество() Цикл
        строспис=рСписок.Получить(й-1);
        //
        комТаблица.Cell(й,1).Range.Text=ПодготовитьТекстовыйФрагмент(строспис.Текст);
        //
        рФорматПараграфа=комТаблица.Cell(й,1).Range.ParagraphFormat;
        рФорматПараграфа.Alignment=?(строспис.Свойство("Выравнивание"),строспис.Выравнивание,рВыравниваниеПоУмолчанию);
        //
        рОтступ=?(строспис.Свойство("Отступ"),строспис.Отступ,рОтступПоУмолчанию);
        Если рОтступ<>-1 Тогда
            рФорматПараграфа.SpaceBeforeAuto=Ложь;
            рФорматПараграфа.SpaceAfterAuto=Ложь;
            рФорматПараграфа.FirstLineIndent=рОтступ;
        КонецЕсли;
        //
        рШрифт=?(строспис.Свойство("Шрифт"),строспис.Шрифт,рШрифтПоУмолчанию);
        комШрифт=комТаблица.Cell(й,1).Range.Font;
        УстановитьСвойстваШрифта(комШрифт,рШрифт);
        //
        Если строспис.Свойство("Заголовок") Тогда
            комТаблица.Cell(й,1).Range.Select();
            рПараметры.КомВорд.Selection.Style=рПараметры.КомДокумент.Styles(строспис.Заголовок);
        КонецЕсли;
        //
        Если строспис.Свойство("Закладка") Тогда
            КомЗакладки=рПараметры.КомДокумент.Bookmarks;
            КомЗакладки.Add(строспис.Закладка.Имя,комТаблица.Cell(й,1).Range);
            КомЗакладки.DefaultSorting=0; //wdSortByName
            КомЗакладки.ShowHidden=Ложь;
            мВсеЗакладки=?(рПараметры.Свойство("ВсеЗакладки"),рПараметры.ВсеЗакладки,Новый Массив);
            мВсеЗакладки.Добавить(Новый Структура("КомДиапазон,Имя",комТаблица.Cell(й,1).Range,строспис.Закладка.Имя));
            рПараметры.Вставить("ВсеЗакладки",мВсеЗакладки);
        КонецЕсли;
        //
        Если строспис.Свойство("Гиперссылка") Тогда
            рДиапазонГиперссылки=НайтиПодстроку(рПараметры.КомДокумент,,строспис.Гиперссылка.Текст);
            рПараметры.КомДокумент.Hyperlinks.Add(рДиапазонГиперссылки,"",строспис.Гиперссылка.ИмяЗакладки,"",строспис.Гиперссылка.Текст);
        КонецЕсли;
        //
    КонецЦикла;
Исключение
    Сообщить("ДобавитьТекст, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
КонецПопытки;
КонецПроцедуры


#Область ПримерВызова_ДобавитьТаблицу
//рШрифтПоУмолчанию=Новый Шрифт("Times New Roman",12); // по умолчанию
//рРазмерОтступа=КомВорд.CentimetersToPoints(1.00);
//
//мспис=Новый Массив(2,2);
//мспис[0][0]=Новый Структура("Картинка,Текст,Шрифт",БиблиотекаКартинок.ЗаставкаВнешнейОперации,"Это заставка 1С",Новый Шрифт(,,,Истина));
//мспис[0][1]=Новый Структура("Картинка,ВыравниваниеТекста,ВыравниваниеКартинки",БиблиотекаКартинок.КомпьютерСервер,wdAlignParagraphRight=2,2);
//мспис[1][0]=Новый Структура("ВложенныйОбъект",рПараметрыВложенногоОбъекта); // обязательно наличие свойства ВидОбъекта, остальное см."пар"-примеры
//мспис[1][1]=Новый Структура("Текст","Просто некое содержание");
// примеры указания гиперссылок-на-закладки см. в выводе текста
//
//мШирин=Новый Массив;
//мШирин.Добавить(190);
//мШирин.Добавить(210);
//
//пар=Новый Структура;
//пар.Вставить("Содержание",мспис);
//пар.Вставить("КомДокумент",d);
//пар.Вставить("ДиапазонПолучатель",КомВорд.Selection.Range);
//пар.Вставить("ВыравниваниеТекста",wdAlignParagraphCenter);
//пар.Вставить("ВыравниваниеКартинки",wdAlignParagraphCenter);
//пар.Вставить("ШиринаТекста",410);
//пар.Вставить("ШрифтПоУмолчанию",рШрифтПоУмолчанию);
//пар.Вставить("ОтступПоУмолчанию",рРазмерОтступа);
//пар.Вставить("ШириныКолонок",мШирин);
//пар.Вставить("БезГраниц",Истина);
//
//ДобавитьТаблицу(пар);
//
#КонецОбласти
//
// Выводит данные из некой коллекции в таблицу документа, допустимы текст, картинки, вложенные объекты.
//
// Картинки выводятся наиболее правильно, если они в формате PNG, для других форматов производится попытка
// преобразовать в этот формат; используется запись во временный файл, так что должны быть права на TMP-папку.
//
// Параметры:
//    рПараметры - структура,
//    обязательно содержащая ключи:
//        КомВорд - собственно COM-объект Word.Application;
//        КомДокумент - объект Document в COM-объекте Word;
//        ДиапазонПолучатель - объект Range в COM-объекте Word;
//        Содержание - коллекция формы или массив структур, т.е. коллекция, имеющая следующий внутренний вид:
//            Картинка - объект Картинка, желательно формата png, может быть наряду с текстом (обтекание по умолчанию);
//            Текст - содержимое выводимого текстового фрагмента (внутреннее форматирование не предусмотрено);
//            Выравнивание - см. ПолучитьВыравнивание, для конкретной ячейки;
//            Отступ - числовое значение отступа абзаца для конкретной ячейки;
//            Шрифт - объект Шрифт, уточняющий шрифт ячейки;
//            Фон - структура с обязательными ключами "Текстура","ЦветТекстуры","ЦветФона" (согласно нотации Word);
//            Заголовок - строка вида "Заголовок 1", "Заголовок 3" итд, определяющая структурный уровень ячейки;
//            Закладка - структура с ключом "Имя", содержащим правильное имя закладки, вставляемой в начало ячейки;
//            Гиперссылка - структура с ключами "ИмяЗакладки" (должна быть такая закладка) и "Текст" (гиперссылки);
//            Вложенный объект - структура параметров, описывающих некий другой объект для вывода в этой ячейке -
//                например, параметры для маркированного списка, который должен быть в этой ячейке;
//                обязательно наличие строкового ключа "ВидОбъекта",
//                допустимые значения: "НумерованныйСписок", "МаркированныйСписок", "Текст", "Таблица".
//    необязательно содержащая ключи:
//        ВыравниваниеКартинки - см. ПолучитьВыравнивание, которое будет установлено для картинок в ячейках таблицы;
//        ВыравниваниеТекста - см. ПолучитьВыравнивание, которое будет установлено для текста в ячейках таблицы;
//        ШрифтПоУмолчанию - объект Шрифт, которым будет выведено содержимое (если нет уточнений);
//        ОтступПоУмолчанию - числовое значение отступа абзаца для содержимого (если нет уточнений).
//        ШириныКолонок - массив, последовательно содержащий числовые ширины колонок таблицы (если не задан, то
//           считается, что колонка в таблице одна и её ширина задаётся как 410 по умолчанию). Ширину менее 20 не ставьте.
//           Без правильно указанного этого массива таблица верно не выведется!
//        БезГраниц - булево, определяет, будет ли выведено обрамление каждой ячейки таблицы.
//
&НаКлиенте
Процедура ДобавитьТаблицу(рПараметры)
Попытка
    рСписок=рПараметры.Содержание;
    комДокумент=рПараметры.КомДокумент;
    //
    рБезГраниц=?(рПараметры.Свойство("БезГраниц"),рПараметры.БезГраниц,Истина);
    рВыравниваниеКартинки=?(рПараметры.Свойство("ВыравниваниеКартинки"),рПараметры.ВыравниваниеКартинки,1);
    рВыравниваниеТекста=?(рПараметры.Свойство("ВыравниваниеТекста"),рПараметры.ВыравниваниеТекста,1);
    рШрифтПоУмолчанию=?(рПараметры.Свойство("ШрифтПоУмолчанию"),рПараметры.ШрифтПоУмолчанию,Неопределено);
    Если ТипЗнч(рШрифтПоУмолчанию)<>Тип("Шрифт") Тогда
        рШрифтПоУмолчанию=Новый Шрифт("Times New Roman",12); // -1, если по умолчанию как в общем стиле
    КонецЕсли;
    рОтступПоУмолчанию=?(рПараметры.Свойство("ОтступПоУмолчанию"),рПараметры.ОтступПоУмолчанию,Неопределено);
    Если ТипЗнч(рОтступПоУмолчанию)<>Тип("Число") Тогда
        рОтступПоУмолчанию=-1;
    КонецЕсли;
    //
    Если рПараметры.Свойство("ШириныКолонок") и ТипЗнч(рПараметры.ШириныКолонок)=Тип("Массив") Тогда
        квоКолонок=рПараметры.ШириныКолонок.Количество();
        комТаблица=комДокумент.Tables.Add(рПараметры.ДиапазонПолучатель,рСписок.Количество(),квоКолонок,1,0);
        Для ы=1 По квоКолонок Цикл
            Попытка
                рШирина=рПараметры.ШириныКолонок.Получить(ы-1);
                Если рШирина<20 Тогда рШирина=20 КонецЕсли; // найдено экспериментально для Word2012
                комТаблица.Columns(ы).SetWidth(рШирина,0);
            Исключение
                Сообщить("Ошибка при установке ширины, равной "+СокрЛП(рШирина)+" для колонки № "+СокрЛП(ы)+", всего колонок "+СокрЛП(квоКолонок)+": "+ОписаниеОшибки(),СтатусСообщения.Важное);
                Прервать;
            КонецПопытки;
        КонецЦикла;
    Иначе // считается, что колонка одна и её ширина задана по умолчанию
        квоКолонок=1;
        комТаблица=комДокумент.Tables.Add(рПараметры.ДиапазонПолучатель,рСписок.Количество(),квоКолонок,1,0);
        рШиринаТекста=?(рПараметры.Свойство("ШиринаТекста"),рПараметры.ШиринаТекста,410);
        комТаблица.Columns(1).SetWidth(рШиринаТекста,0);
    КонецЕсли;
    //
    имяф=ПолучитьИмяВременногоФайла("png");
    Для й=1 По рСписок.Количество() Цикл
        Для ы=1 По квоКолонок Цикл
            элсод=рСписок[й-1][ы-1];
            Если ТипЗнч(элсод)<>Тип("Структура") Тогда Продолжить КонецЕсли;
            //
            Если элсод.Свойство("Картинка") Тогда
                комТаблица.Cell(й,ы).Range.ParagraphFormat.Alignment=?(элсод.Свойство("ВыравниваниеКартинки"),элсод.ВыравниваниеКартинки,рВыравниваниеКартинки);
                рКартинка=элсод.Картинка;
                Если рКартинка.Формат()<>ФорматКартинки.PNG Тогда
                    Попытка
                        рКартинка.Преобразовать(ФорматКартинки.PNG);
                    Исключение
                        Сообщить("Ошибка преобразования формата: "+ОписаниеОшибки(),СтатусСообщения.Важное);
                        Продолжить;
                    КонецПопытки;
                КонецЕсли;
                рКартинка.Записать(имяф);
                комФигура=комТаблица.Cell(й,ы).Range.InlineShapes.AddPicture(имяф,Ложь,Истина);
                //УстановитьОдинаковыеГраницы(комФигура,1); // включить при необходимости
                рПараграф=комТаблица.Cell(й,ы).Range.Paragraphs.Add();
            Иначе
                рПараграф=комТаблица.Cell(й,ы).Range.Paragraphs.Item(1);
            КонецЕсли;
            //
            Если элсод.Свойство("Текст") Тогда
                рПараграф.Range.Text=ПодготовитьТекстовыйФрагмент(элсод.Текст);
                рФорматПараграфа=рПараграф.Format;
                рФорматПараграфа.Alignment=?(элсод.Свойство("ВыравниваниеТекста"),элсод.ВыравниваниеТекста,рВыравниваниеТекста);
                //
                рОтступ=?(элсод.Свойство("Отступ"),элсод.Отступ,рОтступПоУмолчанию);
                Если рОтступ<>-1 Тогда
                    рФорматПараграфа.SpaceBeforeAuto=Ложь;
                    рФорматПараграфа.SpaceAfterAuto=Ложь;
                    рФорматПараграфа.FirstLineIndent=рОтступ;
                КонецЕсли;
                //
                рШрифт=?(элсод.Свойство("Шрифт"),элсод.Шрифт,рШрифтПоУмолчанию);
                комШрифт=рПараграф.Range.Font;
                УстановитьСвойстваШрифта(комШрифт,рШрифт);
            КонецЕсли;
            //
            Если элсод.Свойство("Фон") Тогда
                комФон=комТаблица.Cell(й,ы).Shading;
                комФон.Texture=элсод.Фон.Текстура;
                комФон.ForegroundPatternColor=элсод.Фон.ЦветТекстуры;
                комФон.BackgroundPatternColor=элсод.Фон.ЦветФона;
            КонецЕсли;
            //
            Если элсод.Свойство("Заголовок") Тогда
                комТаблица.Cell(й,ы).Range.Select();
                рПараметры.КомВорд.Selection.Style=рПараметры.КомДокумент.Styles(элсод.Заголовок);
            КонецЕсли;
            //
            Если элсод.Свойство("Закладка") Тогда
                КомЗакладки=рПараметры.КомДокумент.Bookmarks;
                КомЗакладки.Add(элсод.Закладка.Имя,комТаблица.Cell(й,1).Range);
                КомЗакладки.DefaultSorting=0; //wdSortByName
                КомЗакладки.ShowHidden=Ложь;
                мВсеЗакладки=?(рПараметры.Свойство("ВсеЗакладки"),рПараметры.ВсеЗакладки,Новый Массив);
                мВсеЗакладки.Добавить(Новый Структура("КомДиапазон,Имя",комТаблица.Cell(й,1).Range,элсод.Закладка.Имя));
                рПараметры.Вставить("ВсеЗакладки",мВсеЗакладки);
            КонецЕсли;
            //
            Если элсод.Свойство("Гиперссылка") Тогда
                рДиапазонГиперссылки=НайтиПодстроку(рПараметры.КомДокумент,,элсод.Гиперссылка.Текст);
                рПараметры.КомДокумент.Hyperlinks.Add(рДиапазонГиперссылки,"",элсод.Гиперссылка.ИмяЗакладки,"",элсод.Гиперссылка.Текст);
            КонецЕсли;
            //
            Если элсод.Свойство("ВложенныйОбъект") и ТипЗнч(элсод.ВложенныйОбъект)=Тип("Структура") Тогда
                парвлож=элсод.ВложенныйОбъект;
                парвлож.Вставить("ДиапазонПолучатель",комТаблица.Cell(й,ы).Range);
                Если парвлож.ВидОбъекта="НумерованныйСписок" Тогда
                    ДобавитьНумерованныйСписок(парвлож);
                ИначеЕсли парвлож.ВидОбъекта="МаркированныйСписок" Тогда
                    ДобавитьМаркированныйСписок(парвлож);
                ИначеЕсли парвлож.ВидОбъекта="Текст" Тогда
                    ДобавитьТекст(парвлож);
                ИначеЕсли парвлож.ВидОбъекта="Таблица" Тогда
                    ДобавитьТаблицу(парвлож);
                КонецЕсли;
            КонецЕсли;
            //
        КонецЦикла;
    КонецЦикла;
    //
    Если рБезГраниц Тогда
        УстановитьОдинаковыеГраницы(комТаблица);
    КонецЕсли;
    //
Исключение
    Сообщить("ДобавитьТаблицу, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
КонецПопытки;
КонецПроцедуры

#КонецОбласти

Ну и собственно подключение и вызов: 
    Попытка
        КомДокумент=КомВорд.Documents.Add();
        // Здесь выводим нужные данные
        КомВорд.Visible=1;
        КомВорд.Activate();
        КомВорд.ActiveWindow.WindowState=1;
    Исключение
        КомВорд.Quit(0);
        КомВорд=0;
        Сообщить("Ошибка при выводе данных в MS Word: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
    КонецПопытки;

 

Если кому пригодится, то и хорошо.

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Павел Жихарев (palsergeich) 25.08.15 01:13
Замечательная статья, попадись она мне на глаза месяц назад, очень сильно помогла бы, а так как надо было срочно, и со знанием других языков у меня небольшие проблемы, пришлось изобретать велосипеды.
От себя могу добавить, один из костылей, мной используемый, замещающий текст в методе find имеет ограничение по количеству символов, обошел через использование буфера обмена, методом find устанавливаю курсор, и туда вставляю из буфера...
2. velll111 velll111 (velll111) 25.08.15 09:28
Спасибо, очень интересная статья, Ctrl+C и Ctr+V уже сделал)))
3. Евгений (Пользователь 1С) 25.08.15 09:43
С шаблонами проще текст набивать. Здесь, я так понял, весь текст документа нужно программно добавлять. Возможно, удобно реализовать через чтение внешнего текстового файла с заготовками содержания документа. Ну а в целом, полезная статья, спасибо!
4. Алексей 1 (AlX0id) 25.08.15 10:30
По-настоящему тру было бы собрать docx с нуля как xml-ку ) И не надо было бы этих чудо-ком-объектов с их заморочками с правами и разрядностью..
cool.vlad4; N!ghtmare; madonov; +3 Ответить 5
5. Яков Коган (Yashazz) 25.08.15 11:25
(4) AlX0id, да, это было бы реально тру. Но даже простейший экселевский файл так собирать нелегко, много малоизвестных граблей (намучился уже с этим), тем более вордовский - тут надо досконально знать внутреннюю структуру. Ну и с картинками неясно - через Base64 их гонять, что ли? Не факт, что получится.
...а вот интересно, для OpenOffice такое возможно, собрать через xml?
6. Алексей 1 (AlX0id) 26.08.15 23:53
(5) Yashazz,
С картинками-то как раз все довольно понятно - они просто в отдельной папке хранятся в архиве. А в xml-ке документа описываются элементом <w:drawing>. Что, в общем-то легко проверить, создав элементарный документ и посмотрев, что же лежит в архиве )
А по поводу малоизведанных граблей - это, конечно, да, без этого вообще никуда )
Но мне кажется, что задача вполне решаема. по крайней мере, на уровне несложных объектов типа списков, абзацев и т.п. Времени только займет куда как больше, нежели использование чудо-ком-объектов - это точно.
7. Евгений Мадонов (madonov) 31.08.15 07:30
(4) AlX0id, Да!
Давайте напишем свой опен офис на языке 1С!

Начать можно с Word и Excel.

ЗЫ. Особенно забавно было бы реализовать функционал Access внутри 1С - трушность бы просто зашкалила =)) .
investec; lx@; +2 Ответить
8. Сергей Ожерельев (Поручик) 01.09.15 11:38
(4) В одном нашем проекте есть сборка документа из вороха xml на сервере. И оно работает в нескольких организациях.
9. Вадим Латышев (pro1c@inbox.ru) 19.09.15 15:47
Плюсую. Только такое понадобилось, а вы тут как тут!
10. Ийон Тихий (cool.vlad4) 26.10.15 01:23
(4) AlX0id, однозначно. в дотнете например это делается достаточно просто (через openxml sdk , плюс есть еще некоторые библиотеки вокруг этого) . жаль только нет такого для 1С . (можно написать компоненту , иметь лишнюю зависимость и лишний гемор при развертывание решения)
11. Ийон Тихий (cool.vlad4) 26.10.15 01:25
(4) AlX0id, кстати проблема не столько в правах и заморочках com объектов (это было бы полбеды), сколько в конкретной избыточной и тормозной реализации в офисе. многие вещи делаются через Selection , что тормоз, что тот еще глюк . (попробуйте параллельно при формировании большого документа, работать в другом word-е и при это что-то активно копировать и вставлять)
12. Ийон Тихий (cool.vlad4) 26.10.15 01:27
(1) palsergeich, добавлю еще , что при некоторых условиях find иногда в цикле (а если мне не изменяет память, так сделано в типовых на основе БСП) может уходить в бесконечный цикл. такое случается и проблема гуглится.
13. Ийон Тихий (cool.vlad4) 26.10.15 01:37
(5) Yashazz,
...а вот интересно, для OpenOffice такое возможно, собрать через xml?

естественно. это можно сказать под их некоторым влиянием потом уже микрософт решил реализовывать открытый формат openxml для word/excel и т.д. см https://ru.wikipedia.org/wiki/OpenDocument
14. Владислав Лисовенко (VladC#) 10.02.16 05:09
Автор определённо погорячился, добавив в заголовок фразу "по простому", в чём простота, если код занимает несколько экранов?
15. Яков Коган (Yashazz) 10.02.16 18:09
(14) VladC#, ну, наверное, в эксплуатации? )))

...И правда, забавно: привёл, понимаешь, читателям исходник функции, которую 1 раз скопипастить надо, а дальше просто вызывать. Думал, это просто. Но, оказывается, скопипастить - мегасложно. И вызывать потом, следуя нехитрым примерам - архисложно.
Наверное, я-таки погорячился.
investec; +1 Ответить