Кто вообще любит клепать печатные формы? Вот я точно терпеть не могу. Коллега вчера мучился с печатной формой, которую обязательно необходимо было сделать на базе макета в Word.

Я ему помог, показав, как можно сделать макет договора в Word при помощи механизмов DocVariables. Но будем честны — это изобретение велосипеда.

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

Шаг 1. Разработка макета

Вот как он выглядит в Word.

1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация

Полагаю, что и так всё в целом понятно, но давайте всё таки посмотрим на основные фишки.

{v8 Область.ЗдесьПишемИмяОбласти} — начало области

{/v8 Область.ЗдесьПишемИмяОбласти} — это конец области. Обрати внимание на слеш в начале

{v8 ИмяПараметра} — так определяем имя параметра

Области могут быть предопределенными. Такие как колонтитулы верхние, нижние и прочие.

Шаг 2. Разработка внешней обработки.

В модуле объекта внешней обработки пишем данные для подключения нашей ВПФ к командам печати.


Функция СведенияОВнешнейОбработке() Экспорт
	
	ВерсияБСП = "3.0.1.0";
	
	ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке(ВерсияБСП);
	ПараметрыРегистрации.Информация = НСтр("ru = 'Договор (Word)'");
	ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма();
	ПараметрыРегистрации.Версия = "1.0.0.0";
	ПараметрыРегистрации.Назначение.Добавить("Документ.ЗаказПокупателя");
	
	Команда = ПараметрыРегистрации.Команды.Добавить();
	Команда.Представление = НСтр("ru = 'Договор (Word)'");
	Команда.Идентификатор = "ПечатьДоговораWord";
	Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
		
	Возврат ПараметрыРегистрации;
	
КонецФункции

Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
		
	ПечатнаяФорма = УправлениеПечатью.СведенияОПечатнойФорме(КоллекцияПечатныхФорм, "ПечатьДоговораWord");
	Если ПечатнаяФорма <> Неопределено Тогда		
		ОфисныеДокументы = НапечататьДоговор(МассивОбъектов);
		ПечатнаяФорма.СинонимМакета = НСтр("ru = 'Печать договора (Word)'");
		ПечатнаяФорма.ОфисныеДокументы = ОфисныеДокументы;
	КонецЕсли;
	
КонецПроцедуры

Код в том же модуле объекта. Но уже сам код для печати документов.


Функция НапечататьДоговор(МассивОбъектов)
	
	ОфисныеДокументы = Новый Соответствие;
	МакетДокумента = ПолучитьМакет("ПФ_DOCX_Макет");
	Шаблон = НСтр("ru = 'Заказ покупателя №[Номер] от [Дата]'");
	
	Макет = УправлениеПечатью.ИнициализироватьМакетОфисногоДокумента(МакетДокумента, Неопределено);
	Если Макет = Неопределено Тогда
		Возврат "";
	КонецЕсли;
	
	ОписаниеОбластей = Новый Структура;
	УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ВерхнийКолонтитул",       "ВерхнийКолонтитул");
	УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ВерхнийЧетныйКолонтитул", "ВерхнийЧетныйКолонтитул");
	УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "Шапка",         		   "Общая");
	УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "ШапкаТаблицы",            "СтрокаТаблицы");
	УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "СтрокаТаблицы",           "СтрокаТаблицы");
	УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "Подвал",         		   "Общая");
	УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "НижнийКолонтитул",        "НижнийКолонтитул");
	УправлениеПечатью.ДобавитьОписаниеОбласти(ОписаниеОбластей, "НижнийЧетныйКолонтитул",  "НижнийЧетныйКолонтитул");
	
	ДанныеПоШапке = ПолучитьДанныеДляПечати(МассивОбъектов);
	
	Для Каждого ДанныеПечати Из ДанныеПоШапке Цикл
		
		ДанныеПечати.Дата = Формат(ДанныеПечати.Дата, "ДЛФ=DD");

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

Функция ПолучитьДанныеДляПечати(МассивОбъектов)
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
	"ВЫБРАТЬ
	|	&ДатаПечати КАК ДатаПечати,
	|	&Город КАК Город,
	|	ЗаказПокупателя.Дата КАК Дата,
	|	ЗаказПокупателя.Номер КАК Номер,
	|	ЗаказПокупателя.Ссылка КАК Ссылка,
	|	ЗаказПокупателя.Договор.НомерДоговора КАК НомерДоговора,
	|	ЗаказПокупателя.Договор.ДатаДоговора КАК ДатаДоговора,
	|	ЗаказПокупателя.Договор.Владелец.ИНН КАК ИннПокупатель,
	|	ЗаказПокупателя.Договор.Организация.ИНН КАК ИннПоставщик,
	|	ЗаказПокупателя.Контрагент.Наименование КАК Покупатель,
	|	ЗаказПокупателя.Организация.Наименование КАК Поставщик
	|ИЗ
	|	Документ.ЗаказПокупателя КАК ЗаказПокупателя
	|ГДЕ
	|	ЗаказПокупателя.Ссылка В(&МассивДоговоров)";
	
	Запрос.УстановитьПараметр("Город", "г. Петропавловск-Камчатский");
	Запрос.Параметры.Вставить("ДатаПечати", ТекущаяДата());
	Запрос.Параметры.Вставить("МассивДоговоров", МассивОбъектов);
	
	Возврат ОбщегоНазначения.ТаблицаЗначенийВМассив(Запрос.Выполнить().Выгрузить());;
	
КонецФункции

Не забываем вставить макет вордовского файла как Двоичные данные.

1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация

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

1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация
1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация

Не всем может быть очевидно, что в ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары) в качестве данных печати нужно передать массив структур. Для того, чтобы преобразовать табличную часть в Массив Структур можно воспользоваться функцией из пакета БСП ОбщегоНазначения.ТаблицаЗначенийВМассив() и передать туда табличную часть.


ДанныеПечатиТовары = ОбщегоНазначения.ТаблицаЗначенийВМассив(ДанныеПечати.Ссылка.Запасы.Выгрузить());
Область = УправлениеПечатью.ОбластьМакета(Макет, ОписаниеОбластей["СтрокаТаблицы"]);
УправлениеПечатью.ПрисоединитьИЗаполнитьКоллекцию(ПечатнаяФорма, Область, ДанныеПечатиТовары);

Шаг 3. Сделать сносную возможность отладки

Всё конечно круто, но постоянно переподключать ВПФку для отладки — мягко говоря гемморойненько. Давайте допилим форму обработки.

  1. Добавим реквизит ЗаказПокупателя
  2. Создадим форму обработки
  3. Добавим комманду печати
  4. В свойствах формы установим автосохранение реквизита
1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация
1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация
1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация

В модуле формы опишем команду и вызов программы Word.


&НаКлиенте
Процедура Печать(Команда)
	
	ОфисныеДокументы = ПечатьНаСервере();
	
	Для каждого Документ Из ОфисныеДокументы Цикл
		
		ДокументДвоичныеДанные = ПолучитьИзВременногоХранилища(Документ.Ключ);
		ИмяФайла = ПолучитьИмяВременногоФайла(".docx");		
		ДокументДвоичныеДанные.Записать(ИмяФайла);
		
		Word = ПолучитьCOMОбъект(ИмяФайла);
		Word.Application.Visible = 1;
		Word.Activate();
		
	КонецЦикла;
	
КонецПроцедуры

&НаСервере
Функция ПечатьНаСервере()
	
	Обработка = РеквизитФормыВЗначение("Объект");
	
	МассивОбъектов = Новый Массив;
	МассивОбъектов.Добавить(Объект.ЗаказПокупателя);

	ПараметрыПечати = Новый Структура("ДополнитьКомплектВнешнимиПечатнымиФормами", Ложь);
	КоллекцияПечатныхФорм = УправлениеПечатью.ПодготовитьКоллекциюПечатныхФорм("ПечатьДоговораWord");
	
	Обработка.Печать(МассивОбъектов, КоллекцияПечатныхФорм, Неопределено, Неопределено);
	
	Возврат КоллекцияПечатныхФорм[0].ОфисныеДокументы;
	
КонецФункции

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

Вывод картинок

Дополнение к статье с инструкцией как всё таки выводить картинки. В документации к БСП прямо не говориться о том как выводить картинки в макет, однако метод УправлениеПечатьюСлужебный.ЗаполнитьПараметрыОбласти(ПечатнаяФорма, Область, ДанныеОбъекта) нам прямо намекает, что картинки в макет выводить можно.


// УправлениеПечатьюСлужебный.ЗаполнитьПараметрыОбласти(ПечатнаяФорма, Область, ДанныеОбъекта) 

........................

Если ВыводТекста Тогда
	
	ЗаписатьТекстВПотоки(СтруктураРазбораXML, ЧтениеXML, "Область", ТекстУзла, УстановленАтрибутСохраненияПробелов);
	
ИначеЕсли ВыводКартинки Тогда
	
	ДвоичныеДанные = ПолучитьИзВременногоХранилища(ТекстУзла); // ДвоичныеДанные - 
	
	СтруктураКартинка = Новый Структура;
	СтруктураКартинка.Вставить("ДвоичныеДанные",     ДвоичныеДанные);
	СтруктураКартинка.Вставить("ИмяКартинки",        "image");
	СтруктураКартинка.Вставить("КаталогКартинок",    ПечатнаяФорма.СтруктураДокумента.КаталогКартинок);
	
	ПараметрыКартинки = ПолучитьАтрибутыИзображения(ДвоичныеДанные);
	
........................

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

Приступаем. Сначала нужно модифицируем наш макет. Добавим колонку «Изображение» и параметр «Рисунок» (название не важно)

1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация

Вспоминаем, в каком месте у нас был вывод строк табличной части


...............................

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

...............................

ДанныеПечатиТовары — этот массив структур нам нужно дополнить, ссылкой на временное хранилище, которое будет содержать нашу картинку в виде двоичных данных.


Функция НапечататьДоговор(МассивОбъектов)

// Какой то код
............................................

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

............................................
// продолжение какого то кода

КонецФункции

Процедура ДополнитьДаннымиКартинки(МассивДанныеПечатиТовары)
	
	Для каждого Элемент Из МассивДанныеПечатиТовары Цикл
		
		Если Элемент.Номенклатура.ФайлКартинки.Пустая() Тогда
			Картинка = Неопределено;
		Иначе
			Картинка = РаботаСФайлами.ДанныеФайла(Элемент.Номенклатура.ФайлКартинки).СсылкаНаДвоичныеДанныеФайла;			
		КонецЕсли;
		
		Элемент.Вставить("Рисунок", Картинка);
		
	КонецЦикла;
	
КонецПроцедуры

И вуаля!

1С: Внешняя печатная форма Word (.docx) + готовый шаблон — иллюстрация

Заключение

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

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

Файлы для скачивания