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

08.02.24

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

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

Скачать файл

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

Наименование По подписке [?] Купить один файл
Вывод форматированного документа в формате Office open XML (WORD) через сервер с использованием БСП.:
.epf 24,13Kb
6
6 Скачать (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С.

15500 руб.

02.09.2020    183905    1024    403    

967

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

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

8400 руб.

20.08.2024    24154    159    84    

158

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

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

15000 руб.

10.11.2023    13636    57    33    

77

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

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

22200 руб.

06.10.2023    20253    51    19    

86

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

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

9360 руб.

17.05.2024    30477    104    48    

147

Инструментарий разработчика Программист 8.3.14 Россия Платные (руб)

Расширение для конфигурации “Конвертация данных 3”. Добавляет подсветку синтаксиса, детальную контекстную подсказку, глобальный поиск по коду.

20000 руб.

07.10.2021    19090    7    32    

43

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

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

5000 руб.

07.02.2018    104865    246    100    

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