Кто вообще любит клепать печатные формы? Вот я точно терпеть не могу. Коллега вчера мучился с печатной формой, которую обязательно необходимо было сделать на базе макета в Word.
Я ему помог, показав, как можно сделать макет договора в Word при помощи механизмов DocVariables. Но будем честны — это изобретение велосипеда.
1С: Библиотека стандартных подсистем — лучший друг 1С: Программиста. Поэтому давайте переделаем нормально. И разработаем шаблон ВПФ для Word файлов (.docx) для подключения к 1С
Шаг 1. Разработка макета
Вот как он выглядит в Word.

Полагаю, что и так всё в целом понятно, но давайте всё таки посмотрим на основные фишки.
{v8 Область.ЗдесьПишемИмяОбласти} — начало области
{/v8 Область.ЗдесьПишемИмяОбласти} — это конец области. Обрати внимание на слеш в начале
{v8 ИмяПараметра} — так определяем имя параметра
Области могут быть предопределенными. Такие как колонтитулы верхние, нижние и прочие.
Шаг 2. Разработка внешней обработки.
В модуле объекта внешней обработки пишем данные для подключения нашей ВПФ к командам печати.
Функция СведенияОВнешнейОбработке() Экспорт
ВерсияБСП = "3.0.1.0";
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке(ВерсияБСП);
ПараметрыРегистрации.Информация = НСтр("ru = 'Договор (Word)'");
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма();
ПараметрыРегистрации.Версия = "1.0.0.0";
ПараметрыРегистрации.Назначение.Добавить("Документ.ЗаказПокупателя");
Команда = ПараметрыРегистрации.Команды.Добавить();
Команда.Представление = НСтр("ru = 'Договор (Word)'");
Команда.Идентификатор = "ПечатьДоговораWord";
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
Возврат ПараметрыРегистрации;
КонецФункции
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
ПечатнаяФорма = УправлениеПечатью.СведенияОПечатнойФорме(КоллекцияПечатныхФорм, "ПечатьДоговораWord");
Если ПечатнаяФорма <> Неопределено Тогда
ОфисныеДокументы = НапечататьДоговор(МассивОбъектов);
ПечатнаяФорма.СинонимМакета = НСтр("ru = 'Печать договора (Word)'");
ПечатнаяФорма.ОфисныеДокументы = ОфисныеДокументы;
КонецЕсли;
КонецПроцедуры
Код в том же модуле объекта. Но уже сам код для печати документов.
Функция НапечататьДоговор(МассивОбъектов)
ОфисныеДокументы = Новый Соответствие;
МакетДокумента = ПолучитьМакет("ПФ_DOCX_Макет");
Шаблон = НСтр("ru = 'Заказ покупателя №[Номер] от [Дата]'");
Макет = УправлениеПечатью.ИнициализироватьМакетОфисногоДокумента(МакетДокумента, Неопределено);
Если Макет = Неопределено Тогда
Возврат "";
КонецЕсли;
ОписаниеОбластей = Новый Структура;
УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ВерхнийКолонтитул", "ВерхнийКолонтитул");
УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ВерхнийЧетныйКолонтитул", "ВерхнийЧетныйКолонтитул");
УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "Шапка", "Общая");
УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ШапкаТаблицы", "СтрокаТаблицы");
УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "СтрокаТаблицы", "СтрокаТаблицы");
УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "Подвал", "Общая");
УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "НижнийКолонтитул", "НижнийКолонтитул");
УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "НижнийЧетныйКолонтитул", "НижнийЧетныйКолонтитул");
ДанныеПоШапке = ПолучитьДанныеДляПечати(МассивОбъектов);
Для Каждого ДанныеПечати Из ДанныеПоШапке Цикл
ДанныеПечати.Дата = Формат(ДанныеПечати.Дата, "ДЛФ=DD");
ПечатнаяФорма = УправлениеПечатью.ИнициализироватьПечатнуюФорму(Неопределено, Неопределено, Макет);
Если ПечатнаяФорма = Неопределено Тогда
УправлениеПечатью.ОчиститьСсылки(Макет);
Возврат ОфисныеДокументы;
КонецЕсли;
// Вывод верхних колонтитулов
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["ВерхнийКолонтитул"]);
УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати);
// Для четных страниц свои колонтитулы
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["ВерхнийЧетныйКолонтитул"]);
УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати);
// Вывод верхней части документа - обычная область с параметрами.
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["Шапка"]);
УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати);
// Вывод коллекции данных из информационной базы в виде таблицы.
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["ШапкаТаблицы"]);
УправлениеПечатью.ПрисоединитьОбласть(ПечатнаяФорма, Область, Ложь);
// Вывод строка ТЧ
ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить());
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]);
УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары);
// Вывод подвала документа
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["Подвал"]);
УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати);
// Вывод нижних колонтитулов
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["НижнийКолонтитул"]);
УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати);
// Для четных страниц свои колонтитулы
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["НижнийЧетныйКолонтитул"]);
УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеПечати);
АдресХранилищаПечатнойФормы = УправлениеПечатью.СформироватьДокумент(ПечатнаяФорма);
УправлениеПечатью.ОчиститьСсылки(ПечатнаяФорма);
ЗначенияРеквизитовДокумента = Новый Структура("Организация,Номер,Дата,Ссылка");
ЗаполнитьЗначенияСвойств(ЗначенияРеквизитовДокумента, ДанныеПечати);
ЗначенияРеквизитовДокумента.Дата = Формат(ЗначенияРеквизитовДокумента.Дата, "ДЛФ=D");
ЗначенияРеквизитовДокумента.Номер = ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(ЗначенияРеквизитовДокумента.Номер);
ИмяДокумента = СтроковыеФункцииКлиентСервер.ВставитьПараметрыВСтроку(Шаблон, ЗначенияРеквизитовДокумента);
ОфисныеДокументы.Вставить(АдресХранилищаПечатнойФормы, ИмяДокумента);
КонецЦикла;
УправлениеПечатью.ОчиститьСсылки(Макет);
Возврат ОфисныеДокументы;
КонецФункции
Функция ПолучитьДанныеДляПечати(МассивОбъектов)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| &ДатаПечати КАК ДатаПечати,
| &Город КАК Город,
| ЗаказПокупателя.Дата КАК Дата,
| ЗаказПокупателя.Номер КАК Номер,
| ЗаказПокупателя.Ссылка КАК Ссылка,
| ЗаказПокупателя.Договор.НомерДоговора КАК НомерДоговора,
| ЗаказПокупателя.Договор.ДатаДоговора КАК ДатаДоговора,
| ЗаказПокупателя.Договор.Владелец.ИНН КАК ИннПокупатель,
| ЗаказПокупателя.Договор.Организация.ИНН КАК ИннПоставщик,
| ЗаказПокупателя.Контрагент.Наименование КАК Покупатель,
| ЗаказПокупателя.Организация.Наименование КАК Поставщик
|ИЗ
| Документ.ЗаказПокупателя КАК ЗаказПокупателя
|ГДЕ
| ЗаказПокупателя.Ссылка В(&МассивДоговоров)";
Запрос.УстановитьПараметр("Город", "г. Петропавловск-Камчатский");
Запрос.Параметры.Вставить("ДатаПечати", ТекущаяДата());
Запрос.Параметры.Вставить("МассивДоговоров", МассивОбъектов);
Возврат ОбщегоНазначения.ТаблицаЗначенийВМассив(Запрос.Выполнить().Выгрузить());;
КонецФункции
Не забываем вставить макет вордовского файла как Двоичные данные.

Подключаем как ВПФ и радуемся


Не всем может быть очевидно, что в ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары) в качестве данных печати нужно передать массив структур. Для того, чтобы преобразовать табличную часть в Массив Структур можно воспользоваться функцией из пакета БСП ОбщегоНазначения.ТаблицаЗначенийВМассив() и передать туда табличную часть.
ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить());
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]);
УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары);
Шаг 3. Сделать сносную возможность отладки
Всё конечно круто, но постоянно переподключать ВПФку для отладки — мягко говоря гемморойненько. Давайте допилим форму обработки.
- Добавим реквизит ЗаказПокупателя
- Создадим форму обработки
- Добавим комманду печати
- В свойствах формы установим автосохранение реквизита
В модуле формы опишем команду и вызов программы Word.
&НаКлиенте
Процедура Печать(Команда)
ОфисныеДокументы = ПечатьНаСервере();
Для каждого Документ Из ОфисныеДокументы Цикл
ДокументДвоичныеДанные = ПолучитьИзВременногоХранилища(Документ.Ключ);
ИмяФайла = ПолучитьИмяВременногоФайла(".docx");
ДокументДвоичныеДанные.Записать(ИмяФайла);
Word = ПолучитьCOMОбъект(ИмяФайла);
Word.Application.Visible = 1;
Word.Activate();
КонецЦикла;
КонецПроцедуры
&НаСервере
Функция ПечатьНаСервере()
Обработка = РеквизитФормыВЗначение("Объект");
МассивОбъектов = Новый Массив;
МассивОбъектов.Добавить(Объект.ЗаказПокупателя);
ПараметрыПечати = Новый Структура("ДополнитьКомплектВнешнимиПечатнымиФормами", Ложь);
КоллекцияПечатныхФорм = УправлениеПечатью.ПодготовитьКоллекциюПечатныхФорм("ПечатьДоговораWord");
Обработка.Печать(МассивОбъектов, КоллекцияПечатныхФорм, Неопределено, Неопределено);
Возврат КоллекцияПечатныхФорм[0].ОфисныеДокументы;
КонецФункции
Важно понимать, что этот пример сделан с некоторыми упрощениями без акцента на производительность кода.
Вывод картинок
Дополнение к статье с инструкцией как всё таки выводить картинки. В документации к БСП прямо не говориться о том как выводить картинки в макет, однако метод УправлениеПечатьюСлужебный.ЗаполнитьПараметрыОбласти(ПечатнаяФорма, Область, ДанныеОбъекта) нам прямо намекает, что картинки в макет выводить можно.
// УправлениеПечатьюСлужебный.ЗаполнитьПараметрыОбласти(ПечатнаяФорма, Область, ДанныеОбъекта)
........................
Если ВыводТекста Тогда
ЗаписатьТекстВПотоки(СтруктураРазбораXML, ЧтениеXML, "Область", ТекстУзла, УстановленАтрибутСохраненияПробелов);
ИначеЕсли ВыводКартинки Тогда
ДвоичныеДанные = ПолучитьИзВременногоХранилища(ТекстУзла); // ДвоичныеДанные -
СтруктураКартинка = Новый Структура;
СтруктураКартинка.Вставить("ДвоичныеДанные", ДвоичныеДанные);
СтруктураКартинка.Вставить("ИмяКартинки", "image");
СтруктураКартинка.Вставить("КаталогКартинок", ПечатнаяФорма.СтруктураДокумента.КаталогКартинок);
ПараметрыКартинки = ПолучитьАтрибутыИзображения(ДвоичныеДанные);
........................
Расковыряв модуль, можно понять, что для заполнения картинки система хочет получить адрес на временное хранилище с картинкой.
Приступаем. Сначала нужно модифицируем наш макет. Добавим колонку «Изображение» и параметр «Рисунок» (название не важно)

Вспоминаем, в каком месте у нас был вывод строк табличной части
...............................
// Вывод строка ТЧ
ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить());
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]);
УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары);
...............................
ДанныеПечатиТовары — этот массив структур нам нужно дополнить, ссылкой на временное хранилище, которое будет содержать нашу картинку в виде двоичных данных.
Функция НапечататьДоговор(МассивОбъектов)
// Какой то код
............................................
// Вывод строка ТЧ
ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить());
ДополнитьДаннымиКартинки(ДанныеПечатиТовары);
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]);
УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары);
............................................
// продолжение какого то кода
КонецФункции
Процедура ДополнитьДаннымиКартинки(МассивДанныеПечатиТовары)
Для каждого Элемент Из МассивДанныеПечатиТовары Цикл
Если Элемент.Номенклатура.ФайлКартинки.Пустая() Тогда
Картинка = Неопределено;
Иначе
Картинка = РаботаСФайлами.ДанныеФайла(Элемент.Номенклатура.ФайлКартинки).СсылкаНаДвоичныеДанныеФайла;
КонецЕсли;
Элемент.Вставить("Рисунок", Картинка);
КонецЦикла;
КонецПроцедуры
И вуаля!

Заключение
Учитывайте, что приведенный мною код далёк от состояния оптимального и производительного. Здесь я его пытался максимально упростить, чтобы начинающим программистам было проще понять концепцию того, как работает подсистема печати в БСП.
А вообще, разработка печатной формы в MS Word средствами БСП это не сложнее, чем обычная печатная форма. А в некоторых местах даже проще.


