Этюды по программированию. Взаимодействие с Microsoft Word.
Часто приходится заниматься создание сложных документов Word с таблицами, вложенными фрагментами, хитрым оформлением и прочими радостями жизни. Это попытка как то структурировать полученный опыт, чтоб не приходилось перерывать ворох старых обработок в поисках крупиц истины. Надеюсь, эта статья будет полезна и Вам.
Получение шаблона.
В конфигурациях на основе БСП удобно хранить шаблон в справочнике ”Файлы”. Подсистема работы с присоединенными файлами позволяет назначить различные права на папки из этого справочника.
Файл можно найти по имени, или прикрепить как дополнительный реквизит к справочникам и документам. (При создании дополнительного реквизита указать тип “Файл”).
Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | ДополнительныеСведения.Значение КАК Значение |ИЗ | РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения |ГДЕ | ДополнительныеСведения.Свойство.Наименование = &Наименование | И ДополнительныеСведения.Объект = &Ссылка"; Запрос.УстановитьПараметр("Ссылка", ДанныеПечати.Партнер);// Укажите ваш объект с прикрепленным дополнительным реквизитом шаблона. Запрос.УстановитьПараметр("Наименование", "Шаблон спецификации (Сделки с клиентами)"); //Укажите ваше название реквизита РезультатЗапроса = Запрос.Выполнить(); ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать(); Если ВыборкаДетальныеЗаписи.Следующий() Тогда Файл= ВыборкаДетальныеЗаписи.Значение; ДанныеФайлаИДвоичныеДанные = РаботаСФайламиСлужебныйВызовСервера.ДанныеФайлаИДвоичныеДанные(Файл); ДанныеФайла = ДанныеФайлаИДвоичныеДанные.ДанныеФайла; ДвоичныеДанные = ДанныеФайлаИДвоичныеДанные.ДвоичныеДанные; ИмяВременногоФайла = КаталогВременныхФайлов()+"макет.mxl"; ДвоичныеДанные.Записать(ИмяВременногоФайла); Макет =Новый ТабличныйДокумент; Макет.Прочитать(ИмяВременногоФайла); КонецЕсли; КонецЕсли;
Открытие объекта документа из временного файла.
Word = Новый COMОбъект("Word.Application"); Попытка док = Word.Documents.Open(ИмяВременногоФайла); Док.SaveAs(ИмяВременногоФайла); Исключение СообщениеОбОшибке = НСтр("Файл шаблона, указанный в константе, не найден: "+ИмяВременногоФайла+" |Подробности:'") + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()); Word.Quit(); ВызватьИсключение СообщениеОбОшибке; КонецПопытки; РабочийКаталогПользователя = РаботаСФайламиСлужебныйКлиент.РабочийКаталогПользователя(); Word.Visible = 1; Word.Options.CheckSpellingAsYouType = 0; Word.Options.CheckGrammarAsYouType = 0; Word.Options.CheckGrammarWithSpelling = 0; Selection = Word.Selection;
Работа с таблицами по макету таблицы:
1. Макет таблицы. Создаем файл с шаблоном WORD. В файле должна быть таблица с шапкой, пустой строкой и (опционально) подвалом, например следующего вида:
-
- Поиск нужной таблицы в документе. Так как в документе обычно много таблиц, нужную таблицу нужно найти и заполнить
Для Каждого Таб из док.Tables цикл Если СтрНайти(Таб.Cell(1, 1).Range.Text,"№")>0 И СтрНайти(Таб.Cell(1, 2).Range.Text,"Наименование")>0 Тогда //… Заполнение таблицы ТЗ= ПолучитьТЧТовары(Объект.СсылкаНаОбъект); Таб.Rows(Таб.Rows.Count).Select(); //Выделям строку, на место которой будем вставлять новые строки //Таб.Rows(1).Select(); Строка=Таб.Rows.Count; Итерация=1; Для Каждого стр Из ТЗ Цикл Если Итерация>1 Тогда Selection.InsertRowsBelow( 1); // КонецЕсли; Таб.Cell(Строка, 1).Range.Text = стр.НомерСтроки; Таб.Cell(Строка, 2).Range.Text = стр.НоменклатураНаименование+" "+ТекстовоеОписаниеБезКомплектации(стр.ТекстовоеОписание); Таб.Cell(Строка, 2).Range.Paragraphs.Alignment = 0; текКол = ?(стр.Количество = 0,1,стр.Количество ); Таб.Cell(Строка, 3).Range.Text =?((стр.ЦенаВключаетНДС), Формат((стр.Сумма - стр.СуммаНДС), "ЧДЦ=2"),Формат(стр.Сумма, "ЧДЦ=2")); Таб.Cell(Строка, 3).Range.Paragraphs.Alignment = 1; Таб.Cell(Строка, 4).Range.Text =Формат(стр.СуммаНДС, "ЧДЦ=2"); Таб.Cell(Строка, 4).Range.Paragraphs.Alignment = 1; Таб.Cell(Строка, 5).Range.Text = ?((стр.ЦенаВключаетНДС), Формат((стр.Сумма ), "ЧДЦ=2"),Формат((стр.Сумма+ стр.СуммаНДС), "ЧДЦ=2")); Таб.Cell(Строка, 5).Range.Paragraphs.Alignment = 1; Строка = Строка + 1; Итерация=Итерация+1; КонецЦикла; КонецЦикла;
Работа с таблицами 2. Вставляем невидимую таблицу по месту закладки:
-
Очень часто нужно вставить невидимую таблицу в ячейку другой таблицы, например для вывода номенклатуры и количества:
Если Док.Bookmarks.Exists("ТаблицаПродукции") Тогда Поз = Док.GoTo(-1,,, "ТаблицаПродукции"); Колонок = 2; Таб = Док.Tables.Add(Поз, 1, Колонок); //Таб.Cell(1, 1).Range.Text = "Наименование"; //Таб.Cell(1, 2).Range.Text = "Кол-во"; //Таб.Cell(1, 3).Range.Text = "Ед."; Таб.Range.Paragraphs.Alignment = 1; Таб.Columns(1).Width=270; Таб.Columns(2).Width=80; Строка = 1; Для Каждого стр Из ДанныеПоБП.НоменклатураБрак Цикл Таб.Rows.Add(); Таб.Cell(Строка, 1).Range.Text = стр.Наименование; Таб.Cell(Строка, 2).Range.Text =""+ стр.КоличествоБрака+""; Таб.Cell(Строка, 2).Range.Paragraphs.Alignment = 0; Строка = Строка + 1; КонецЦикла; КонецЕсли;
-
Работа с таблицами 3. Объединяем одинаковые значения в столбцах:
-
Пример для вывода такой таблицы:
-
Стоимость:
Материал, плотность
Печать
Лак
Тираж, в тыс. шт.
Количество печатных листов
Стоимость одной тыс. шт., с НДС в руб.
Нева 3201
СМУК
ВД глянц. выб.
200
1
4407,97
Нева 320
ВД глянц. выб. 1
СМУК2
ВД глянц. выб.
-
//... ОбъединениеСтрокВКолонке(Таб, ТЗ,3,"Лак"); ОбъединениеСтрокВКолонке(Таб, ТЗ,2,"Печать"); ОбъединениеСтрокВКолонке(Таб, ТЗ,1,"МатериалПлотность"); //... &НаКлиенте Процедура ОбъединениеСтрокВКолонке(Знач Таб, Знач ТЗ,НомерКолонки,ИмяКолонки) Перем Y, НачалоВертикальногоБлока, п1, СодержимоеЯчейки, СодержимоеЯчейкиПредыдущей, Х; СодержимоеЯчейкиПредыдущей=""; КоличествоСтрок=ТЗ.Количество(); НачалоВертикальногоБлока=КоличествоСтрок; НачалоВертикальногоБлокаПредыдущее=КоличествоСтрок; КоличествоИзменений=0; Для Y =1 по КоличествоСтрок Цикл YR= КоличествоСтрок-Y+1; СодержимоеЯчейки=ТЗ[YR-1][ИмяКолонки]; ИзменениеСодержимого=Ложь; Если СодержимоеЯчейки<> СодержимоеЯчейкиПредыдущей Тогда КоличествоИзменений=КоличествоИзменений+1; ИзменениеСодержимого=Истина; СодержимоеЯчейкиПредыдущей=СодержимоеЯчейки; НачалоВертикальногоБлокаПредыдущее=НачалоВертикальногоБлока; НачалоВертикальногоБлока=YR; КонецЕсли; Если ИзменениеСодержимого И НачалоВертикальногоБлокаПредыдущее-НачалоВертикальногоБлока>1 Тогда п1=Таб.cell(НачалоВертикальногоБлокаПредыдущее+2, НомерКолонки); Попытка // эта конструкция выдает исключительную ситуацию, но при этом // результат все-равно работает)) // объединяем ячейки в конце таблицы Таб.cell(НачалоВертикальногоБлока+3, НомерКолонки).Merge(п1); Исключение КонецПопытки; Таб.cell(НачалоВертикальногоБлока+3, НомерКолонки).Range.Text=ТЗ[YR][ИмяКолонки]; ИначеЕсли YR=1 И НачалоВертикальногоБлока-YR>=1 Тогда п1=Таб.cell(YR+2, НомерКолонки); Попытка // эта конструкция выдает исключительную ситуацию, но при этом // результат все-равно работает)) // объединяем ячейки в конце таблицы Таб.cell(НачалоВертикальногоБлока+2, НомерКолонки).Merge(п1); Исключение КонецПопытки; п1.Range.Text=ТЗ[YR][ИмяКолонки]; КонецЕсли КонецЦикла; КонецПроцедуры
- Вставляем фрагмент из другого документа WORD:
- Обеспечиваем в нужном месте шаблона закладку.Процедура для вставки фрагмента:
-
Процедура ВставитьПодшаблонИзСправочникаФайл(ИмяЗакладки,Word) Selection = Word.Selection; Для Каждого Закладка из Word.ActiveDocument.Bookmarks Цикл Если Найти(Закладка.Name,ИмяЗакладки) Тогда Файл = НайтиФайлПодшаблонаНаСервере(); Если ЗначениеЗаполнено(Файл) Тогда ДанныеФайлаИДвоичныеДанные = РаботаСФайламиСлужебныйВызовСервера.ДанныеФайлаИДвоичныеДанные(Файл); ДанныеФайла = ДанныеФайлаИДвоичныеДанные.ДанныеФайла; ДвоичныеДанные = ДанныеФайлаИДвоичныеДанные.ДвоичныеДанные; Попытка ИмяВременногоФайлаШаблона = КаталогВременныхФайлов()+"Шкафы КРУ сборка1.doc"; ДвоичныеДанные.Записать(ИмяВременногоФайлаШаблона); Исключение Попытка ИмяВременногоФайлаШаблона = КаталогВременныхФайлов()+"Шкафы КРУ сборка2.doc"; ДвоичныеДанные.Записать(ИмяВременногоФайлаШаблона); Исключение КонецПопытки; КонецПопытки; Ворд2 = Новый COMОбъект("Word.Application"); Попытка док2 = Ворд2.Documents.Open(ИмяВременногоФайлаШаблона); НомерСтрокиТаб=3; Для Каждого Таб из док2.Tables цикл //заполняем таблицу КонецЦикла; Ворд2.ActiveDocument.Select(); Ворд2.Selection.Copy(); Закладка.Range.Select(); Selection.paste(); Ворд2.ActiveDocument.Close(0); Ворд2.Quit(); Исключение ПредупреждениеСерв("Файл не найден: "+ИмяВременногоФайлаШаблона); Ворд2.Quit(); КонецПопытки; Попытка УдалитьФайлы(ИмяВременногоФайлаШаблона); Исключение ПредупреждениеСерв(ОписаниеОшибки()); КонецПопытки; КонецЕсли; КонецЕсли; КонецЦикла; КонецПроцедуры
Замена текста шаблона на нужный нам текст WORD:- Предназначенный для замены текст шаблона ограничиваем квадратными скобками. Вместо текста пишем название маркера, по которому мы будем находить этот фрагмент.
- В 1С создаем таблицу маркеров, которая будет содержать два обязательных поля “ Маркер” и “Значение”
-
Вызываем следующий код:
Для Каждого стр Из Маркеры Цикл
Если ТипЗнч(стр.Значение) = Тип("Дата") Тогда
Значение = СокрЛП(Формат(стр.Значение, "ДФ=""dd.MM.yyyy 'г.'"""));
Иначе
Значение = СокрЛП(стр.Значение);
КонецЕсли;
док.content.find.execute("["+стр.Маркер+"]",,,,,,,,, Строка(Значение), 2);
док.Sections(1).Headers(1).Range.Find.Execute("["+стр.Маркер+"]",,,,,,,,, Строка(Значение), 2);
КонецЦикла; Вставка рисунка из макета:
- Создаем в конфигураторе макет-двоичные данные, в него загружаем нужный рисунок.
- Вызываем функцию, текст которой приведен ниже. В качестве аргумента selection удобно передавать выделение ячейки таблицы, это позволяет красиво расположить рисунок в тексте. Сама таблица может быть и невидимой. Например, следующий вызов:
///...
ВставитьМакет( Док,Таб.Cell(Строка, 3).Range, ИмяМакета);
///...
&НаКлиенте
Функция ВставитьМакет( Знач Док,Знач Selection,Знач ИмяМакета)
Перем Picture, Shape;
Попытка
ИмяВременногоФайла= СохранитьМакетВоВременныйФайл(ИмяМакета);
Picture = Selection.InlineShapes.AddPicture(ИмяВременногоФайла,, Истина);
//Picture.LockAspectRatio = -1; //сохрняем пропорции
Picture.Height= 150;
Picture.Width = 150; //устанавливаем ширину
// Чтобы установить обтекание текста, конвертируем рисунок в фигуру
Shape = Picture.ConvertToShape();
Shape.WrapFormat.Type = 5;// перед текстом...
Исключение
Сообщить("Не удалось подгрузить временный файл:"+ИмяВременногоФайла);
КонецПопытки;
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
Сообщить("Не удалось удалить временный файл:"+ИмяВременногоФайла+"
|"+ОписаниеОшибки());
КонецПопытки;
// Зададим размер
Возврат Selection;
КонецФункции
P.S.: Надеюсь, вам понравится эта и другие мои статьи и разработки на //infostart.ru/profile/48714/.