При формировании печатных форм в формате Word (DOCX) средствами Библиотеки Стандартных Подсистем (БСП) разработчики часто сталкиваются с неприятной проблемой: при попытке вывести картинку (например, логотип компании) в верхний или нижний колонтитул, итоговый документ вместо изображения отображает пустую рамку с текстом «Сейчас не удается отобразить рисунок». При этом та же самая картинка в теле документа выводится абсолютно корректно.
В этой статье мы подробно разберем, почему стандартные механизмы 1С дают сбой, проанализируем внутреннюю структуру документа Word и рассмотрим надежные способы решения этой задачи — от «хитростей» при подготовке макета до серьезной программной правки механизмов БСП.
Прежде чем переходить к коду, давайте разберем «под капотом», как устроен файл DOCX. По сути, это ZIP-архив, внутри которого находятся XML-файлы. Когда мы вставляем картинку в колонтитул, Word выполняет следующие действия:
word/media/.header1.xml) создает ссылку на это изображение через идентификатор rId.word/_rels/header1.xml.rels) прописывает соответствие между этим rId и физическим путем к файлу в папке media.Проблема заключается в том, что механизмы БСП (подсистема Печать, модуль УправлениеПечатью) при сборке финального документа корректно обрабатывают связи только для основного текста (файл document.xml.rels). Файлы связей для колонтитулов зачастую либо игнорируются, либо перезаписываются без учета новых вставленных картинок. В результате в документе остается ссылка на картинку, но Word не может найти файл связи, чтобы понять, какой именно рисунок нужно показать.
Если вы не хотите вносить изменения в программный код системы, можно воспользоваться административным обходным путем. Рассмотрим его по шагам:
_rels для колонтитула.{v8 Картинка:Логотип}.При таком подходе БСП не создает новый объект связи, а пытается заменить бинарные данные уже существующей картинки, что повышает шансы на успех.
Для версий БСП 3.1.7 и близких к ним, наиболее надежным решением является корректировка служебных процедур, которые отвечают за инициализацию структуры печатной формы и сборку контейнера DOCX. Нам необходимо заставить систему видеть и сохранять файлы .rels для колонтитулов.
Рассмотрим пример реализации в расширении. Нам потребуется заимствовать модуль УправлениеПечатьюСлужебный и переопределить в нем логику формирования структуры документа.
Проанализируем процедуру инициализации структуры. В стандартном варианте она «забывает» прочитать файлы связей для заголовков. Добавим этот функционал:
&ИзменениеИКонтроль("ИнициализироватьСтруктуруПечатнойФормы")
Процедура ПФ_ИнициализироватьСтруктуруПечатнойФормы(ПечатнаяФорма, Макет)
// ... (стандартный код инициализации каталогов)
КаталогФайлаСвязей = ИмяКаталога + УстановитьРазделительПути("\word\_rels\");
// Работаем с файлами в каталоге word
КаталогСоСтруктуройФайла = ИмяКаталога + "word" + ПолучитьРазделительПути;
СтруктурныеФайлы = НайтиФайлы(КаталогСоСтруктуройФайла, "*.xml");
Для Каждого Файл Из СтруктурныеФайлы Цикл
// Обработка верхних колонтитулов (headers)
Если Лев(Файл.ИмяБезРасширения, 6) = "header" Тогда
ФайлСвязей = Новый Файл(КаталогФайлаСвязей + Файл.Имя + ".rels");
Если ФайлСвязей.Существует Тогда
ЧтениеXML = Новый ЧтениеТекста(ФайлСвязей.ПолноеИмя, КодировкаТекста.UTF8);
ТекстФайла = ЧтениеXML.Прочитать();
// Сохраняем связи во временную структуру для дальнейшей сборки
СтруктураДокумента.Вставить("СвязиКонтентаВерхнегоКолонтитула", ТекстФайла);
КонецЕсли;
КонецЕсли;
КонецЦикла;
// ... (продолжение стандартной логики)
КонецПроцедуры
Далее необходимо доработать функцию СформироватьПечатнуюФорму, чтобы при сохранении документа данные из нашей временной структуры СвязиКонтентаВерхнегоКолонтитула физически записывались обратно в файл header1.xml.rels внутри итогового пакета.
Разберем фрагмент кода, который должен быть вставлен в процесс сборки контента:
// Вставляем блок записи связей перед финальной сборкой контейнера
Если ПечатнаяФорма.СтруктураДокумента.Свойство("СвязиКонтентаВерхнегоКолонтитула") Тогда
ИмяФайлаСвязей = ПечатнаяФорма.ИмяКаталога + "word\_rels\header1.xml.rels";
ЗаписьXML = Новый ЗаписьТекста(ИмяФайлаСвязей, КодировкаТекста.UTF8);
ЗаписьXML.Записать(ПечатнаяФорма.СтруктураДокумента.СвязиКонтентаВерхнегоКолонтитула);
ЗаписьXML.Закрыть();
КонецЕсли;
Если быстродействие не является критическим фактором, а механизмы БСП в вашей конфигурации слишком сильно кастомизированы, можно использовать прямое управление приложением Microsoft Word через COM-соединение. Этот метод работает медленнее, но он использует родные методы Word, которые гарантированно корректно вставляют изображения.
Рассмотрим пример кода для вставки картинки в нижний колонтитул первой секции документа:
Процедура ВставитьКартинкуЧерезCOM(ПутьКФайлу, ПутьККартинке)
Попытка
MsWord = Новый COMОбъект("Word.Application");
MsWord.Visible = Ложь;
Документ = MsWord.Documents.Open(ПутьКФайлу);
// Получаем доступ к нижнему колонтитулу первой секции
Колонтитул = Документ.Sections.Item(1).Footers.Item(1);
// Вставляем картинку как InlineShape
Колонтитул.Range.InlineShapes.AddPicture(ПутьККартинке, Ложь, Истина);
Документ.Save();
Документ.Close();
MsWord.Quit();
Исключение
Сообщить("Ошибка при работе с Word: " + ОписаниеОшибки());
Если ЗначениеЗаполнено(MsWord) Тогда
MsWord.Quit();
КонецЕсли;
КонецПопытки;
КонецПроцедуры
Важное примечание: Использование COMОбъект требует установленного Microsoft Office на сервере (если код выполняется на стороне сервера) или на клиенте. В облачных сервисах или при работе через веб-клиент этот метод работать не будет.
Проанализировав все варианты, можно сделать следующие выводы:
{v8 Картинка:ИмяПараметра} в любом месте документа.Следуя этим инструкциям, вы сможете формировать профессиональные печатные формы с логотипами и подписями в колонтитулах, которые будут корректно открываться у всех пользователей.