gifts2017

Работа с MS Word из 1С "для самых маленьких"

Опубликовал Виталий Попов (Сурикат) в раздел Программирование - Практика программирования

Данная статья - попытка собрать сведения по работе (на начальном уровне) с MS Word в одном месте. На infostart.ru и других сайтах полно похожего материала, и при наличии должной усидчивости это все можно найти. Хотелось собрать основные моменты в одном месте.

Публикация - своего рода памятка, содержащая примеры кода для:

1. заполнение шаблона Word данными из 1С;
2. заполнение колонтитулов Word данными из 1С;
3. заполнение таблицы в Word данными из 1С;

Начало работы

В большинстве случаев перед нами ставится следующая задача: 
Нужно открыть документ Word, заполненный данными из 1С.

Для этого нам нужно подготовить шаблон документа Word. Не путайте это с Word Template, специальные файлы Word, которые содержат настройки документов для многократного использования. Нам нужен обычный вордовский документ с расширениеми *.docx или *.doc.  А далее поместить этот документ в макет с двоичными данными.

Попытка
 //Получаем макет из двоичных данных
   Шаблон = ПолучитьМакет(ДополнительныеПараметры.ВидДоговора);
 ИмяФайла = ПолучитьИмяВременногоФайла(".docx");
   Шаблон.Записать(ИмяФайла);
 //Создаем COM-объект для работы с Word
   ОбъектВорд = Новый COMОбъект("Word.Application");
   ОбъектВорд.Documents.add(ИмяФайла);
 //В Word можно открывать в одном приложении несколько документов, поэтому ОбъектВорд.Application.Documents - это коллекция открытых документов.
 //В нашем случае документ открыт всегда один
   ДокументВорд = ОбъектВорд.Application.Documents(1);
   ДокументВорд.Activate();
   Исключение Сообщить("Ошибка при запуске приложения "+ОписаниеОшибки());
 КонецПопытки;
//Разумеется, получение COM-объекта нужно поместить в попытку. Мало ли, что-то пойдет не так.

Читатель может справедливо заметить, что используется модальный вызов, и погрозить автору пальцем. И будет прав.

//Создадим структуру параметров документа
 ПараметрыДокумента = ПодготовитьСтрукутруПараметров();
 //Заполним структуру параметров документа
 ЗаполнитьСтруктуруПараметров(ПараметрыДокумента);

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

Углубимся немножко в принципы работы Word...

Каждый документ Word разделен на разделы, которые состоят из страниц.

Для каждого раздела есть возможность создавать свою нумерацию элементов, уникальные колонтитулы и настройки параметров страницы. Так, например, чтобы повернуть одну из страниц (вывести на печать как альбомную), нужно создать под неё отдельный раздел.


Каждая страница Word разделена на несколько областей:

  • Верхний колонтитул 
  • Основной текст
  • Нижний колонтитул

Нужно заметить, что в каждом разделе может быть уникальный колонтитул для первой страницы.

//Объект, содержит весь основный текст из всех разделов ДокументВорд.Content
//Объект содержит коллекцию разделов документа
ДокументВорд.Sections
//В каждом разделе есть своя коллекция для верхних колонтитулов
ДокументВорд.Sections(1).Headers
//И своя коллекция для нижних колонтитулов
ДокументВорд.Sections(1).Footers
//При этом, если стоит галочка "Уникальный колонтитул для первой страницы", то коллекции Headers и Footers будут содержать два элемента
 

Заполнение пользовательских параметров

При обращении к этим коллекциям мы можем выполнять в них поиск и получать встроенные объекты, например, таблицы.

Теперь мы более-менее поняли, как обращаться к областям Word, можем в них пошуровать и выполнить замену наших параметров:

//Переберем все параметры и заменим их в документе
Для каждого Параметр Из ПараметрыДокумента Цикл
  ВыполнитьЗамену(ДокументВорд.Content, Параметр.Ключ, Параметр.Значение);
//Ищим вхождения параметра в верхнем колонтитуле
  ВыполнитьЗамену(ДокументВорд.Sections(1).Headers.Item(1).Range(), Параметр.Ключ, Параметр.Значение);
//Ищим вхождения параметра в нижнем колонтитуле первой и последующих страниц               
  ВыполнитьЗамену(ДокументВорд.Sections(1).Footers.Item(1).Range(), Параметр.Ключ, Параметр.Значение);    
  ВыполнитьЗамену(ДокументВорд.Sections(1).Footers.Item(2).Range(), Параметр.Ключ, Параметр.Значение);
КонецЦикла;

//Выполнить поиск и замену Функция
ВыполнитьЗамену(знач Object, Параметр, Значение)
  Object.Find.Execute(Параметр,,,,,,,,,Значение,2)
КонецФункции
 

Рассмотри подробнее метод Execute. Его параметры идентичны диалоговуму окну при замене/поиске непоседресвенно из MS Word: 

А вот и основные параметры (вольный перевод справки MSDN):

  1. Искомый текст - Строка - Текст для замены. Текст может содержать специальные параметры. Например, ^p - абзац, ^t - табуляция
  2. Чувствительность к регистру - Булево - Если истина, то поиск будет осуществляться с учетом регистра
  3. Слова целиком - Булево - Если истина, то ищутся слова целиком. Вхождение слов не учитываются. Например, при поиске слова дом будет пропущено слово домашний
  4. Использовать подстановочные знаки - Булево - Если истина, то используются встроенные регулярные выражения.
  5. Искать похожие - Булево - Если истина, то результат поиска будет содержать похожие слова
  6. Искать все формы - Булево - Если истина, то результат поиска будет содержать различные формы слов.
  7. Поиск сначала - Булево - Если истина, то будет осуществляться с начала до конца документа
  8. Охват - WdFindWrap - Опредяляет направление поиска
  9. Формат - Format - Формат искомого текста
  10. Строка замены - Строка - Строка, на которую будет заменен исходный текст
  11. Количество замен - WdReplace - Определяет сколько раз выполнять замену
  12. и т.д.

WdReplace - Constant Value:
wdReplaceAll 2 
wdReplaceNone 0 
wdReplaceOne 1

Данный метод не позволяет получить "Строка замены" как выделенную область, но он работает где-то в 10 раз медленнее. Для получения выделенной области можно воспользоваться немножко откорректированной типовой функцией:

//УправлениеПечатьюMSWordКлиент c незначительными изменениями для конфигурации УПП 1.3
Функция ВыполнитьЗамену(знач Object, Параметр, Значение)
  СтрокаПоиска = "[" + Параметр + "]";
  СтрокаЗамены = Строка(Значение);
//Необходимо выделить областей, в которой мы осуществляем замену
  Object.Select();
//Получаем выделенную область
  Selection = Object.Application.Selection;
//Найдем все вхождения параметра и заменим его на нужное нам значение
  FindObject = Selection.Find;
  FindObject.ClearFormatting();
  Пока FindObject.Execute(СтрокаПоиска) Цикл
    Если ПустаяСтрока(СтрокаЗамены) Тогда
      Selection.Delete();
    Иначе
      Selection.TypeText(СтрокаЗамены);
    КонецЕсли;
  КонецЦикла;
//Отменим выделение
  Selection.Collapse();
КонецФункции

Уже получив выделенную область можно отредактировать стиль текста, шрифт и т.д.

//Редактирование шрифта
 Selection.Font
//Редактирование цвета
 Selection.HighlightColorIndex

Также есть второй подход, использующий такой объект Word, как поля. Мне он не очень нравится, т.к. в больших документах, порядка 100 страниц, эти поля начинают глючить (исчезать, не подставлять нужные значения) и прочая ерунда. Ну по крайней мере в Word 2007. Но я его все равно приведу:  

При подготовке шаблона в тело документа необходимо навставлять полей с типом DOCVARIABLE (можно вставлять горячими клавишими Ctrl+F9).

Доступ к таким полям можно получить следующим нехитрым образом:

ДокументВорд.Variables.Item(НазваниеПараметра).Values

Заполнение таблиц по шаблону

Итак, мы заполнили параметры в основном тексте документа, заменили параметры в колонтитулах, но у нас еще есть одна неприятность - нужно заполнить таблицу.

Подход, описанный ниже, годится только для таблиц с заранее известным форматом. Т.е. мы можем как угодно отформатировать таблицу и её строки изначально. Но потом изменять довольно-таки проблематично.

К таблицам можно получить доступ через области документа.

//Получаем доступ к первой таблице в основном тексте
 Таблица = ДокументВорд.Content.Tables(1)

Далее, получив таблицу, мы работаем с ней по привычной схеме - строки, столбцы.

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

//Данные для заполнения
  ПараметрыТЧ = ПараметрыДокумента.ПриложениеТЧ;
//Нужно оставить шапку и первую строку нетронутой Итератор = 2;
  Таблица = ДокументВорд.Content.Tables(3);
  Для каждого Строка Из ПараметрыТЧ Цикл
//По умолчанию добавляет строку выше первой
    Таблица.Rows.Add();
    ЗаполнитьСтрокуТаблицы(Таблица, Итератор, Строка,"ПП,НоменклатураНаименование,ЕдИзмерения,ЦенаСтрокой",ПараметрыТЧ );
    Итератор = Итератор + 1;
  КонецЦикла;
//Структура шаблонов содержит Наименование колонок и их порядок
  Процедура ЗаполнитьСтрокуТаблицы(Таблица, НомерСтроки, ЗначениеЗаполнения, СтруктураШаблонов,ТаблицаЗначений)
    МассивСтрок = ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(СтруктураШаблонов,",");
    Итератор = 1;
    Для каждого Строка Из МассивСтрок Цикл
      Если ТаблицаЗначений.Колонки.Найти(Строка) = Неопределено И Строка <> "ПП" Тогда
        Итератор = Итератор + 1;
        Продолжить;
   //Не забываем, что Шапка тоже строка, а при нумерации нам нужно её исключить
      ИначеЕсли Строка = "ПП" Тогда
        Таблица.Cell(НомерСтроки, Итератор).Range().Text = Строка(НомерСтроки-1);
        Итератор = Итератор + 1;
        Продолжить;
      КонецЕсли;
      Таблица.Cell(НомерСтроки, Итератор).Range().Text = Строка(ЗначениеЗаполнения[Строка]);
      Итератор = Итератор + 1;
   КонецЦикла;
КонецПроцедуры
 

Вот, в принципе, и все. Основные вопросы, возникающие при работе с Word, я постарался осветить. Надеюсь, данный обзор поможет вам в работе =)

Спасибо за советы и комментарии:
v3rtermonkbest

Критика только приветствуется. Чем больше замечаний, тем лучше будет гайд =)

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Максим Сафонов (maXon777) 12.05.16 10:30
Прекрасный обзор! Спасибо!

Очень понравился подход с программным заполнением таблицы путем добавления строк!
2. GENNADIUS O (GOOD256) 12.05.16 13:52
3. Павел Мокосеев (pvl_mksv) 12.05.16 14:25
4. Константин Нагибович (gradi) 12.05.16 16:44
В ворд2013 глюков с DOCVARIABLE не замечал.
5. DUH Technolover (DJDUH) 12.05.16 17:14
Самая "засада" MSWord - это вставить значение в шейпы или иные надписи и картинки - сколько не игрался так и не получилось, найти выход - находит нужное, а значение вставить фига(
6. Валерий Гайдабура (director04) 12.05.16 20:25
Поставил плюс за краткость. И хотел бы еще одни плюс за заголовок.
Хотя,... есть тут пара засранцев, которые обязательно нагадят, что де "все это всем давно известно..."
Спасибо, плюс
7. Виталий Попов (Сурикат) 12.05.16 23:17
(4) gradi,
На небольших документов действительно все хорошо работает (на небольших я имею ввиду несколько десятков страниц). Когда количество приближается к 100 возможно непонятное поведение параметров. По использованию полей во мне больше говорит пользователь, который в свое время много с ними мучился =)
8. Виталий Попов (Сурикат) 12.05.16 23:22
(5) DJDUH,
К сожалению или к счастью с таким не сталкивался =)
Могу только посоветовать покурить MSDN в части свойств и методов указанных вами коллекций. Если получиться, дайте знать. Добавлю к публикации =) Ну или сами созреете для написания кратенького гайда =)
9. Константин Нагибович (gradi) 13.05.16 13:15
(5) DJDUH, у вас не получалось именно вставить значение или добиться его отображения в документе?
10. Наталья Николаева (nni93) 13.05.16 15:30
11. Максим Андрияшин (AndMax) 17.05.16 14:43
Коротко и красиво, спасибо, +
12. Леонид Стасюков (stilet) 18.05.16 11:05
Подскажите, может кто нибудь сталкивался - как изменить цвет фона у какого нибудь набора символов (скажем двух или трех слов)
13. Виталий Попов (Сурикат) 18.05.16 11:36
(12) stilet, а он у вас все время разный?
14. Роберт В е р т и н с к и й (v3rter) 18.05.16 12:24
Базовый метод работы с Word - записываем макрос с нужными действиями, читаем его, читаем хелп к нужным командам, чистим макрос от лишних действий и параметров, копируем в 1С, добавляя "ДокументВорд." перед командами.

По поводу параметров - был и есть старый дедовский метод, когда параметр вставлялся в виде текста типа [Мой_параметр], {Мой_параметр}, [{(%%Мой_параметр%%)}] - на выбор, затем командной поиска и замены самого ворда параметры меняются на значения.

Кстати, проверяли: Application.Visible = False вначале и Application.Visible = True в конце действий ускоряет или нет?
Zhilyakovdr; gradi; monkbest; +3 Ответить 1
15. Роберт В е р т и н с к и й (v3rter) 18.05.16 12:27
(12) stilet, если выделенного текста желтым, то
Selection.Range.HighlightColorIndex = wdYellow
16. Сергей Курышов (sudmorsh) 18.05.16 16:18
Очень полезно, спасибо ;)
17. Роберт В е р т и н с к и й (v3rter) 18.05.16 16:40
Записал в ворде макрос замены текста во всем документе, кроме колонтитулов.

Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "что"
.Replacement.Text = "на что"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.EscapeKey

Возможно, будет полезен.

Из хелпа:
WdReplace
Constant Value
wdReplaceAll 2
wdReplaceNone 0
wdReplaceOne 1
Сурикат; +1 Ответить 1
18. Антон Антонов (monkbest) 19.05.16 08:07
(17) v3rter, зачем искать в цикле?
я делаю так
ТекДок.Content.Find.Execute("%параметр%,,,,,,,,,"мой текст",2);

меняет сразу во всем документе, работает гораздо быстрее, чем искать в цикле
gigapevt; Сурикат; +2 Ответить 1
19. Роберт В е р т и н с к и й (v3rter) 19.05.16 10:04
(18) monkbest, ну так я на это и намекал. Ждём от автора версию 2.0
20. Виталий Попов (Сурикат) 22.05.16 14:09
(14) v3rter,
Application.Visible = False вначале и Application.Visible = True в конце действий не ускоряет процесс. Проверил =)
21. Инесса Умарова (isanka) 23.05.16 14:02
Спасибо за труды.
Для старта очень познавательно.
Вопрос.
как прочитать текст содержащийся в полученной таблице ?
и как заполнять таблицы в ворде данными не переписывая строки самой таблицы, а просто заполняя ее данными?
(пробовала заполнять в цикле (Таблица.Cell(ИндексСтрока+1,ИндексСтолбец+1).Range().InsertAfter(Текст),
так очень долго, да и местами таблицы уже частично заполнены, может есть возможность загрузить данные в таблицу с заранее известным количеством строк и столбцов?
или это из области фантастики?)


22. Виталий Попов (Сурикат) 23.05.16 18:10
Приношу извинения за небольшие проблемы с форматированием =(( Подвел новый редактор.
23. Виталий Попов (Сурикат) 23.05.16 18:12
(21) isanka,
Мне другие способы не знакомы. Можно заполнять же только некоторые ячейки. Обращение по номеру строки/столбца это позволяет.
Поищите как word с ADO дружит =) В Excel ADO дает неплохой прирост производительности. Если найдете, дайте знать =) Добавим в публикацию =))
24. Роберт В е р т и н с к и й (v3rter) 24.05.16 16:16
(21) isanka, Попробуйте Таблица.Cell(ИндексСтрока+1,ИндексСтолбец+1).Range().Text или Таблица.Cell(ИндексСтрока+1,ИндексСтолбец+1).Range.Text

Всё это гуглится, но не на русском: https://msdn.microsoft.com/ru-ru/library/office/ff821612.aspx
25. Максим Иванов (freud) 23.11.16 19:54
windows 10 + ms office
1c выдает

ОбъектВорд = Новый COMОбъект("Word.Application")


Не удалось создать объект.
Возможно, отсутствует соответствующее приложение.

в чем причина
26. sssssssssssssss sssssssssssssss (sssss_aaaaa_2011) 23.11.16 20:33
(25) В том, что последние версии оффиса не имеют СОМ-объектов.
27. Виталий Попов (Сурикат) 25.11.16 08:38
(25)А где выполняется код? на сервере или на клиенте?