Вывод форматированного документа в формате Office open XML (WORD) через сервер с использованием БСП

08.02.24

Разработка - Инструментарий разработчика

Стаяла задача выводить много разных форматированных документов в ворд, пришлось для этого немного "покурить" Office Open XML.  У меня получилось следующее решение.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Вывод форматированного документа в формате Office open XML (WORD) через сервер с использованием БСП.:
.epf 24,13Kb
5
5 Скачать (1 SM) Купить за 1 850 руб.

В макете ворда делаем определенный тег для форматированного документа, у меня это  {v8 ФорматированныйДокумент}

Далее в коде после получения области

		Область = УправлениеПечатью.ОбластьМакета(Макет, Области[ИмяМакета].ТекстФорматированногоДокумента); 

Находим область и заменяем ее на наш сгенерированный xml код и присоединяем область.

		ТекстОбласти = Область.Текст;
		
		СтрокаПоискаБезТегов = "ФорматированныйДокумент"; 
		СтрокаПоиска = "{v8 " + СтрокаПоискаБезТегов + "}"; 
		
		НачалоОписания = СтрНайти(ТекстОбласти, СтрокаПоиска);    
		Описание = ЭтотОбъект.Описание;
		
		НомерКартинки = 0;
		
		Пока НачалоОписания <> 0 Цикл 
			
			ЗаменаОписанияНаХМЛ(НачалоОписания, ТекстОбласти, СтрокаПоиска, СтрокаПоискаБезТегов, 
			Описание, ДанныеОбъекта, НомерКартинки);
			
		КонецЦикла;        
		
		Область.Текст = ТекстОбласти;
		
		УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, Область, ДанныеОбъекта);
Процедура ЗаменаОписанияНаХМЛ(НачалоОписания, ТекстОбласти, СтрокаПоиска, СтрокаПоискаБезТегов, Описание, ДанныеОбъекта, НомерКартинки)
	
    ТекстХМЛ = "";  
	ТекстИзменение = ТекстОбласти; 
	
	НачалоТекстаЗамены = СтрНайти(ТекстИзменение, "<w:p ", НаправлениеПоиска.СКонца, НачалоОписания);
	КонецТекстаЗамены = СтрНайти(ТекстИзменение, "</w:p>", НаправлениеПоиска.СНачала, НачалоОписания);
	Длина = КонецТекстаЗамены - НачалоТекстаЗамены;
	ТекстЗамены = Сред(ТекстИзменение, НачалоТекстаЗамены, Длина + 6);


	КонецПараграфа = СтрНайти(ТекстЗамены, ">");   
	Параграф = Лев(ТекстЗамены, КонецПараграфа);  
	
	НачалоСвойстваРаздела = СтрНайти(ТекстИзменение, "<w:pPr>", НаправлениеПоиска.СКонца);
	СвойстваРаздела = Сред(ТекстИзменение, НачалоСвойстваРаздела);  
	КонецСвойстваРаздела = СтрНайти(СвойстваРаздела, "</w:pPr>");   
	СвойстваРаздела = Лев(СвойстваРаздела, КонецСвойстваРаздела + 7); 
	
	ЗапускТекста = "<w:r>";
	Текст = "<w:t>";

	Для каждого ПараграфФорматированногоДокумента из Описание.Элементы Цикл
		ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, Параграф); 
		ТекущееСвойствоРаздела = СвойстваРаздела;	
		Выравнивание = ПараграфФорматированногоДокумента.ГоризонтальноеПоложение; 

		Если Выравнивание <> ГоризонтальноеПоложение.Авто Тогда
			Центр = "center";  
			Лево = "left";
			Право = "right";
			ПоШирине = "both"; 
			
			Если Выравнивание = ГоризонтальноеПоложение.Лево Тогда
				ТекущиеВырванивание = Лево;		
			КонецЕсли; 
			
			Если Выравнивание = ГоризонтальноеПоложение.Право Тогда
				ТекущиеВырванивание = Право;		
			КонецЕсли;
			
			Если Выравнивание = ГоризонтальноеПоложение.Центр Тогда
				ТекущиеВырванивание = Центр;		
			КонецЕсли;  
			
			Если Выравнивание = ГоризонтальноеПоложение.ПоШирине Тогда
				ТекущиеВырванивание = ПоШирине;		
			КонецЕсли;
		
			НачалоСвойстваРаздела = СтрНайти(ТекущееСвойствоРаздела, "<w:jc w:val=""", НаправлениеПоиска.СКонца); 
			Если НачалоСвойстваРаздела <> 0  Тогда
				
				ВыравниваниеСвойства = Сред(ТекущееСвойствоРаздела, НачалоСвойстваРаздела + 13);  
				КонецСвойстваРаздела = СтрНайти(ВыравниваниеСвойства, """/>");   
				ВыравниваниеСвойства = Лев(ВыравниваниеСвойства, КонецСвойстваРаздела - 1); 
				ТекущееСвойствоРаздела = СтрЗаменить(ТекущееСвойствоРаздела, ВыравниваниеСвойства, ТекущиеВырванивание); 
				
			Иначе
				
				ВыравниваниеНовое = СтрШаблон("<w:jc w:val=""%1""/>", ТекущиеВырванивание);  
				ТекущееСвойствоРаздела = СтрЗаменить(ТекущееСвойствоРаздела, "<w:pPr>", "<w:pPr>" + ВыравниваниеНовое);
				
			КонецЕсли;
			
		КонецЕсли;    
		
		Если ПараграфФорматированногоДокумента.ТипПараграфа = ТипПараграфа.МаркированныйСписок Тогда
			
			НумерованныйСписок = "<w:pPr><w:numPr><w:ilvl w:val=""0""/><w:numId w:val=""1""/></w:numPr>";
			ТекущееСвойствоРаздела = СтрЗаменить(ТекущееСвойствоРаздела, "<w:pPr>", НумерованныйСписок);
			
		ИначеЕсли ПараграфФорматированногоДокумента.ТипПараграфа = ТипПараграфа.НумерованныйСписок Тогда
			
			НумерованныйСписок =  "<w:pPr><w:numPr><w:ilvl w:val=""0""/><w:numId w:val=""2""/></w:numPr>";
			ТекущееСвойствоРаздела = СтрЗаменить(ТекущееСвойствоРаздела, "<w:pPr>", НумерованныйСписок);
			
		КонецЕсли; 
		
		ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, ТекущееСвойствоРаздела);
		
		Для каждого ЭлементыПараграфа Из ПараграфФорматированногоДокумента.Элементы Цикл
			
			
			ТипТексФД = Тип("ТекстФорматированногоДокумента");
			ТипКартинкаФД = Тип("КартинкаФорматированногоДокумента");
			ТипПереводСтрокиФД = Тип("ПереводСтрокиФорматированногоДокумента");
			ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, ЗапускТекста);
			
			
			Если ТипЗнч(ЭлементыПараграфа) = ТипТексФД Тогда
								
				ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, "<w:rPr>");

				Шрифт = ЭлементыПараграфа.Шрифт;   
				ЦветТекста = ЭлементыПараграфа.ЦветТекста; 
				ЦветФона = ЭлементыПараграфа.ЦветФона;  
				
				Если ЦветТекста <> Новый Цвет() Тогда
					
					РГБЦвет = ЦветТекста.ПолучитьАбсолютный();
					
					Зеленый = РГБЦвет.Зеленый; 
					Красный = РГБЦвет.Красный; 
					Синий = РГБЦвет.Синий;
					
					ХЕКСЦВЕТ = "#" + Сред(DecToAny(ПобитовоеИли(ПобитовоеИли(ПобитовоеИли(Синий, ПобитовыйСдвигВлево(Зеленый, 8)), ПобитовыйСдвигВлево(Красный, 16)), ПобитовыйСдвигВлево(1, 24)), 16), 2);
					СтрокаТега = СтрШаблон("<w:color w:val=""%1""/>", ХЕКСЦВЕТ);	
					ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, СтрокаТега);
					
				КонецЕсли; 
				
				Если ЦветФона <> Новый Цвет() Тогда
					
					РГБЦвет = ЦветФона.ПолучитьАбсолютный();
					
					Зеленый = РГБЦвет.Зеленый; 
					Красный = РГБЦвет.Красный; 
					Синий = РГБЦвет.Синий;
					
					ХЕКСЦВЕТ = "#" + Сред(DecToAny(ПобитовоеИли(ПобитовоеИли(ПобитовоеИли(Синий, ПобитовыйСдвигВлево(Зеленый, 8)), ПобитовыйСдвигВлево(Красный, 16)), ПобитовыйСдвигВлево(1, 24)), 16), 2);
					СтрокаТега = СтрШаблон("<w:shd w:val=""clear"" w:color=""auto"" w:fill=""%1""/>", ХЕКСЦВЕТ);	
					ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, СтрокаТега);
					
				КонецЕсли;
				
				ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, "<w:rFonts w:ascii=""Times New Roman"" w:hAnsi=""Times New Roman"" w:cs=""Times New Roman""/>");	
				
				
				Если Шрифт.Полужирный <> Неопределено И Шрифт.Полужирный Тогда
					ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, "<w:b/>");		
				КонецЕсли;
				
				Если Шрифт.Наклонный <> Неопределено И Шрифт.Наклонный Тогда
					ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, "<w:i/>");		
				КонецЕсли;
				
				Если Шрифт.Подчеркивание <> Неопределено И Шрифт.Подчеркивание Тогда
					ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, "<w:u w:val=""single""/>");		
				КонецЕсли;  
				ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, "</w:rPr>");
				ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, Текст + ЭлементыПараграфа.Текст +ЗакрывающийТег(Текст));     
				
			КонецЕсли;   
			
			Если ТипЗнч(ЭлементыПараграфа) = ТипКартинкаФД Тогда 
				
				СтруктураКартинки = Новый Структура("АдресКартинки");
				НомерКартинки = НомерКартинки + 1;    
				
				Если ТипЗнч(ЭлементыПараграфа.Картинка) = Тип("Картинка") Тогда
					КартинкаДвоичныеДанные = ЭлементыПараграфа.Картинка.ПолучитьДвоичныеДанные();
					
				ИначеЕсли СтрНайти(ЭлементыПараграфа.Картинка, "https://") Тогда
					
					АдресФайла = ЭлементыПараграфа.Картинка;
					Защита = Новый ЗащищенноеСоединениеOpenSSL(Неопределено, Неопределено);
					СтруктураУРИ = ОбщегоНазначенияКлиентСервер.СтруктураURI(АдресФайла);
					Попытка
						Соединение = Новый HTTPСоединение(СтруктураУРИ.ИмяСервера, , , , , , Защита);
					Исключение
						Сообщить(ОписаниеОшибки());
					КонецПопытки;
					Запрос = Новый HTTPЗапрос(СтруктураУРИ.ПутьНаСервере);
					Ответ = ОтправитьЗапрос(Соединение, Запрос);
					Если Ответ.КодСостояния = 200 Тогда
						//Как то обрабатываем результат
						КартинкаДвоичныеДанные = Ответ.ПолучитьТелоКакДвоичныеДанные();
						//АдресХранилища = ПоместитьВоВременноеХранилище(ДД, Новый УникальныйИдентификатор);
					Иначе
						
						КартинкаДвоичныеДанные = ЭлементыПараграфа.Картинка; 
						
				 КонецЕсли;
				 
				Иначе
					
					//Если ЭлементыПараграфа.Высота > 0 И ЭлементыПараграфа.Ширина > 0 Тогда 
					//	Высота = ЭлементыПараграфа.Высота/4;
					//	Ширина = ЭлементыПараграфа.Ширина/4;
					//	//КоэффициентПересчета = 914400/2.54/10*1000	
					//	Если Ширина > 160  Тогда
					//		СтруктураКартинки.Вставить("Ширина", 160);
					//		СтруктураКартинки.Вставить("Высота", Высота/(Ширина/160));	
					//	Иначе
					//		СтруктураКартинки.Вставить("Ширина", Ширина);
					//		СтруктураКартинки.Вставить("Высота", Высота);
					//	КонецЕсли;
					//КонецЕсли;
					СтрокаКартинка = ЭлементыПараграфа.Картинка;
					СтрокаКартинка = СтрЗаменить(СтрокаКартинка, "data:image/png;base64,", "");	
					КартинкаДвоичныеДанные = Base64Значение(СтрокаКартинка);
				КонецЕсли;
				
				ТегКартинки = СтрШаблон("{v8 КартинкаФД_%1_Файл}", НомерКартинки);   
				КлючСтроки = СтрШаблон("КартинкаФД_%1_Файл", НомерКартинки); 
				ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, Текст + ТегКартинки +ЗакрывающийТег(Текст));
				СтруктураКартинки.АдресКартинки = ПоместитьВоВременноеХранилище(КартинкаДвоичныеДанные);    
				
				ДанныеОбъекта.Вставить(КлючСтроки, СтруктураКартинки);
			КонецЕсли;
			
			ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, ЗакрывающийТег(ЗапускТекста));
			
		КонецЦикла; 
		ТекстХМЛ = ДобавитьТекст(ТекстХМЛ, "</w:p>"); 
		
	КонецЦикла;    
	
	ТекстОбласти = СтрЗаменить(ТекстОбласти, ТекстЗамены, ТекстХМЛ); 

	НачалоОписания = 0;
	
	Для каждого ЭлементыСтруктуры Из ДанныеОбъекта Цикл
		
		Если СтрНайти(ЭлементыСтруктуры.Ключ, "ФорматированныйДокумент") = 0  Тогда
			Продолжить;
		КонецЕсли;
		
		Если СтрНайти(ТекстОбласти, ЭлементыСтруктуры.Ключ) <> 0  Тогда
			
			НачалоОписания = СтрНайти(ТекстОбласти, ЭлементыСтруктуры.Ключ);	
			СтрокаПоиска = "{v8 " + ЭлементыСтруктуры.Ключ + "}";;
			СтрокаПоискаБезТегов = ЭлементыСтруктуры.Ключ;  
			Описание = ЭлементыСтруктуры.Значение;
			Возврат;
			
		КонецЕсли;	
		
	КонецЦикла;
	
КонецПроцедуры

Функция ЗакрывающийТег(Тег)
	
	Возврат СтрЗаменить(Тег, "<w", "</w") 	
	
КонецФункции // ()

Функция ДобавитьТекст(ТекстХМЛ, ДополнительныеДанные)
	
	Возврат ТекстХМЛ + ДополнительныеДанные; 	
	
КонецФункции // ()

Функция DecToAny(Знач тЗначение, тОснование)
	
	тРезультат = "";
	
	Пока тЗначение > 0 Цикл
		тРезультат = Сред("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", тЗначение%тОснование + 1, 1) + тРезультат;
		тЗначение = Цел(тЗначение/тОснование) ;
	КонецЦикла;
	
	Возврат тРезультат;
	
КонецФункции            

Функция ОтправитьЗапрос(Соединение, Запрос)
	Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос);
	Если Ответ.КодСостояния = 200 Тогда
        //Выходим из рекурсии
		Возврат Ответ;
	ИначеЕсли Ответ.КодСостояния > 300 И Ответ.КодСостояния < 400 Тогда
		//Редирект
		Ответ = ОбработатьРедирект(Соединение, Ответ);
	ИначеЕсли Ответ.КодСостояния >= 400 Тогда
		//Обработка ошибки
		Возврат Ответ;
	КонецЕсли;
	Возврат Ответ;
КонецФункции               

Функция ОбработатьРедирект(Соединение, Ответ)

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

Чтобы узнать, какой код необходим, надо разархивировать макет в формате docx, тогда в появившейся папке находим файл numbering.xml и оттуда берем необходимые параметры.

У меня получилось так:

 

 

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

Использовала следующие статьи и обработки на Инфостарте:

//infostart.ru/1c/tools/860960/

//infostart.ru/1c/tools/1324110/

//infostart.ru/1c/articles/1915944/

Тестировалось на "Библиотека стандартных подсистем", редакция 3.1 (3.1.9.104).

Проверено на следующих конфигурациях и релизах:

  • 1С:ERP Управление предприятием 2, релизы 2.5.14.74

Форматированный документ WORD Office open XML БСП

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Руководитель проекта Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Платные (руб)

Инструменты для разработчиков 1С 8.3: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

12000 руб.

02.09.2020    171667    960    403    

924

Инструментарий разработчика Чистка данных Свертка базы Инструменты администратора БД Системный администратор Программист Руководитель проекта Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Россия Платные (руб)

Инструмент представляет собой обработку для проведения свёртки или обрезки баз данных. Работает на ЛЮБЫХ конфигурациях (УТ, БП, ERP и т.д.). Поддерживаются серверные и файловые базы, управляемые и обычные формы. Может выполнять свертку сразу нескольких баз данных и выполнять их автоматически без непосредственного участия пользователя. Решение в Реестре отечественного ПО

8400 руб.

20.08.2024    14239    108    46    

108

Инструментарий разработчика Программист Платформа 1С v8.3 1C:Бухгалтерия Платные (руб)

Инструмент для написания и отладки кода в режиме «1С:Предприятие». Представляет собой консоль кода с возможностью пошаговой отладки, просмотра значений переменных любых типов, использования процедур и функций, просмотра стека вызовов, вычисления произвольных выражений на встроенном языке в контексте точки останова, синтаксического контроля и остановки по ошибке. В консоли используется удобный редактор кода с подсветкой, контекстной подсказкой, возможностью вызова конструкторов запроса и форматной строки.

9360 руб.

17.05.2024    27236    96    48    

137

Пакетная печать Печатные формы Инструментарий разработчика Программист Платформа 1С v8.3 Запросы 1С:Зарплата и кадры бюджетного учреждения 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 Платные (руб)

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

22200 руб.

06.10.2023    17247    43    15    

75

Инструменты администратора БД Инструментарий разработчика Роли и права Программист Платформа 1С v8.3 1C:Бухгалтерия Россия Платные (руб)

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

15000 руб.

10.11.2023    11883    45    27    

67

SALE! %

Инструментарий разработчика Инструменты администратора БД Системный администратор Программист Платформа 1С v8.3 Управляемые формы 1C:Бухгалтерия Россия Платные (руб)

Универсальный инструмент программиста для администрирования конфигураций. Сборник наиболее часто используемых обработок под единым интерфейсом.

4800 3840 руб.

14.01.2013    191146    1152    0    

920

Инструментарий разработчика Платформа 1С v8.3 1C:Бухгалтерия 1С:ERP Управление предприятием 2 Платные (руб)

Разработка Конструктор автоматизированных рабочих мест "Конструктор АРМ" реализована в виде расширения и является универсальным инструментом для создания АРМ любой сложности в пользовательском режиме.

3600 руб.

27.12.2024    1111    2    0    

5

Инструментарий разработчика Программист Платформа 1С v8.3 1C:Бухгалтерия Россия Платные (руб)

Восстановление партий или взаиморасчетов, расчет зарплаты, пакетное формирование документов или отчетов - теперь все это стало доступнее. * Есть желание повысить скорость работы медленных алгоритмов! Но... * Нет времени думать о реализации многопоточности? * о запуске и остановке потоков? * о поддержании потоков в рабочем состоянии? * о передаче данных в потоки и как получить ответ из потока? * об организации последовательности? Тогда ЭТО - то что надо!!!

5000 руб.

07.02.2018    104110    244    100    

307
Оставьте свое сообщение