Статья состоит из:
1) Теория и общее описание алгоритма.
2) Описание использования
3) Код методов
4) Рабочая обработка — пример использования под 8.2 (обычные формы)
Моя первая публикация (чего-то полезного) не только на инфостарте, но вообще в интернете, так что особо сильно не пинайте
Тем, кому не интересна теория, предлагаю сразу перейти ко 2-му пункту, быстро прочесть как пользоваться и скопировать код методов из 3-го пункта. Некоторое время после написания публикации я буду находиться на поддержке, отвечать на вопросы (возможно исправлять баги).
Исправил редкий баг, всем кто пользовался до «10.06.2014 13:03:56» рекомендую обновить метод РаспарситьСтроку(Стр, ХешКодировки)
1. Теория и общее описание алгоритма.
Сразу хочу заметить, что не претендую на самое красивое, лучшее и оптимальное решение этой задачи, все ниже описанное можно и нужно оптимизировать и я даже знаю как, но только руки не доходят. Формат RTF очень старый и имеет много надстроек и дополнений, всю его спецификацию я разумеется не изучал. Если простыми словами, то основа формата блоки заключенные в фигурные скобки, вот такие «{ }». Внутри блока расположены команды-маркеры, которые начинаются с символа «» и описывают шрифт, цвет фона текста и т.д. т.п. и текст (если блок текстовый). Таблицы в RTF легко склеиваются между собой достаточно просто в тексте удалить все символы, разделяющие таблицы. Честно говоря, иметь счастье разбиаться со спецификацией таблиц у меня не было ни какого желанию, по этому решено было использовть свойство склейки, просто повторяющуюся многократно строку необходимо заключить в секцию и многократно вывести. О секциях расскажу ниже. На этом рассмотрение теории по RTF можно закончить. Вы и сами можете по исследовать этот вопрос, создав файл *.rtf и открыв его хоть блокнотом, хоть Hex редактором. Алгоримтм на самом деле банален и прост. Читаем файл, превращаем в текст. Далее работаем с текстом. В первом приближении это всего два метода. Первый — это построить дерево из блоков, команд и текста. Второй — это превратить дерево в текст классическим рекурсивным обходом. Вот текст методов:
Функция РаспарситьСтрокуПерваяВерсия(Стр)
Перем Дерево, Стек, Мас, Анализ, Секции, Параметры;
Стек = Новый Массив;
Дерево = Новый Массив;
Мас = Дерево;
НачатоСлово = Ложь;
БылСлеш = ложь;
Темп = "";
Блок = Новый Структура("Дерево, Родитель");
ХешКодировки = ПолучитьХешКодировки();
Для х = 1 по СтрДлина(Стр) Цикл
Символ = Сред(Стр, х, 1);
Если НачатоСлово Тогда
Если Символ = " " Тогда
НачатоСлово = Ложь;
Темп = Темп + Символ;
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Темп = "";
Мас.Добавить(Анализ);
Продолжить;
КонецЕсли;
Если Символ = "{" Тогда
Если БылСлеш Тогда
Темп = Темп + Символ;
Иначе
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Мас.Добавить(Анализ);
Темп = "";
НачатоСлово = Ложь;
БылСлеш = Ложь;
Блок = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, ?(Стек.Количество() = 0, Неопределено, Стек[Стек.ВГраница()]));
Стек.Добавить(Блок);
Мас.Добавить(Блок);
Мас = Блок.Дерево;
Продолжить;
КонецЕсли;
КонецЕсли;
Если Символ = "}" Тогда
Если БылСлеш Тогда
Темп = Темп + Символ;
Иначе
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Мас.Добавить(Анализ);
Темп = "";
НачатоСлово = Ложь;
БылСлеш = Ложь;
Стек.Удалить(Стек.ВГраница());
Если Стек.Количество() = 0 Тогда
Мас = Дерево;
Иначе
Мас = Стек[Стек.ВГраница()].Дерево;
КонецЕсли;
Продолжить;
КонецЕсли;
КонецЕсли;
Если Символ = "" Тогда
Если БылСлеш Тогда
БылСлеш = Ложь;
Иначе
БылСлеш = Истина;
КонецЕсли;
Иначе
БылСлеш = Ложь;
КонецЕсли;
Темп = Темп + Символ;
Иначе
Если Символ = "{" Тогда
Блок = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, ?(Стек.Количество() = 0, Неопределено, Стек[Стек.ВГраница()]));
Стек.Добавить(Блок);
Мас.Добавить(Блок);
Мас = Блок.Дерево;
Продолжить;
КонецЕсли;
Если Символ = "}" Тогда
Стек.Удалить(Стек.ВГраница());
Если Стек.Количество() = 0 Тогда
Мас = Дерево;
Иначе
Мас = Стек[Стек.ВГраница()].Дерево;
КонецЕсли;
Продолжить;
КонецЕсли;
Если Символ <> " " Тогда
Темп = Символ;
НачатоСлово = Истина;
Если Символ = "" Тогда
БылСлеш = Истина;
Иначе
БылСлеш = Ложь;
КонецЕсли;
Продолжить;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Дерево;
КонецФункции
и метод преобразования дерева в строку, совсем уж маленький
Функция СформироватьСтроку(Дерево) Экспорт
Перем рет, Элем;
рет = ""; Стек = Новый Массив;
Для каждого Элем из Дерево Цикл
Если Элем.Тип = "Блок" Тогда
рет = рет + "{" + СформироватьСтроку(Элем.Дерево) + "}";
Иначе
рет = рет + Элем.Текст;
КонецЕсли;
КонецЦикла;
Возврат рет;
КонецФункции
Далее все усложняется тем, что необходимо помимо обычного текста отслеживать секции и параметры, запоминать их засосвывать в какую-нибудь структуру. В какую только ? Размумеется в такую, что бы было максимально просто и удобно работать как проффесионалу, так и новичку (ну что бы мозг не напрягался). В начале я думал построить такое же дерево, но это не удобано. в результате пришел к тому, что проще всего сделать Хеш (но 1С Соотвестсвие), в котром можно обратиться к нужному элементу (секции или параметру) по имени. Причем элемент Хеша это массив, на случай если в макете будут встречаться параметры или секции несколько раз. В результате, 1 раз заполнив параметр, он автоматический заполниться по всему документу, аналогично и относительно секции. Далее еще 2 метода, первый — это копирование секции, зная где ее начало и где конец, и второй — это заполнение параметра (просто подмена текста). Секции при копировании пропускают все управляющие блоки, т.е. теги секций и параметров. Все эти операции происходят прямо в дереве значений. Дерево по сути — это структура с большим количеством коротких строк, работать с которыми 1С-ке гораздо проще (я правда не знаю какой алгоритм для работы со строками использует фирма 1С, но в любом случае оперировать строками порядка 1-го мегобайта это не по феншую). В результате на пентиуме 4, шаблон размером под 1 мегобайт (почти 40 страниц) заполняется всего за 1 минуту. Для тех кто разберется со спецификацией RTF бонус в том, что уже все разложено по полочкам и можно менять шрифт, цвет и прочие параметры текста прямо на лету в самом шаблоне, хотя это можно делать и на уровне секций, например создать чередующуюся цветами таблицу.
2. Описание использования.
Описание использования состоит из 2-х частей: правила составления макетов (для пользователей) и правила вывода макетов (для программистов)
правила составления макетов (для пользователей)
- Секции оформляются 2-мя тегами: откытием секции и закрытием секции (Пример:…).
- Параметры оформляются одним тегом через квадратные скобки (Пример: [Параметр1]).
- Любой текст за пределами секции принадлежит документу и будет выводиться в любом случае (Пример: Этот текст выведется в любом случаеЭтот текст выведется только если будет выведена секция «ШапкаДокумента» значение параметра «тестовый» = [тестовый])
- Секции могут быть вложенными. (Пример:Шапка:Шапка:)
- Макет может состоять из произвольного количества секций и параметров. Правило составления секции аналогично правило расставлению скобок в обычно тексте. Нельзя что бы в любом месте количество закрывающих скобок было больше открывающих. На параметры нет никаких ограничений.
- Можно создавать параметры с одинаковым именем. При этом все параметры с одинаковым именем будут заполнены одним значением (Пример: Дата документа = [ДатаДокумента], и тут тоже быдет выведена такая же дата документа [ДатаДокумента]).
- Можно создавать секции с одинаковым именем, даже вложенные одна в другую. При этом они будут выведен столько раз, сколько задано в алгоритме. (Для примера предположим, что секция «Тестовая» выведена будет 2 раза и текст макета следующий: «1. Текст секции2. Текст вложенной». При таких условиях результатом будет текст: «1. Текст секции 2. Текст вложенной 2. Текст вложенной 1. Текст секции 2. Текст вложенной 2. Текст вложенной «)
- Правила составления имени секции или параметра. Можно использовать пробелы в имени (но не рекомендуется). Имена регистрозависимы ([ИмяПараметра] и [имяпараметра] это 2 разных параметра). Можно смело использовать цифры и спец символы, кроме «<«, «>», «[«, «]» или последовательности «
- Таблицы. Таблица формируется внутри секции. Телом секции является одна строка таблицы с параметрами. После объявления начала секции не должно быть ни текста ни пробелов, только перевод на новую строку. Аналогично для строки таблицы с параметрами и следующим за ней объявлением конца секции.
- Возможные ошибки. Не обязательно, но крайне рекомендуется обрамлять пробелами все теги секций и параметров (Пример: «Пробел»»Пробел» текст секции «пробел»[Параметр]»пробел» конец текста секции «пробел»»Пробел»
правила вывода макетов (для программистов)
-
Работа начинается с получения Хеша кодировки (Соответствия) функцией ПолучитьХешКодировки(). Все остальные функции используют этот Хеш.
-
Далее используется функция РаспарситьСтроку(Стр, ХешКодировки), Стр — это обычная строка (полученная считыванием файла-шаблона RTF). В результате возвтращается структура с 2-мя параметрами «Дерево» и «Секции». Дерево — это макет RTF в виде древовидной структуры. Секции — структура секций и параметров макета.
-
Далее вызывая методы ЗаполнитьПараметр(СтруктураСекций, ИмяПараметра, ЗначениеЗаполнения, ХешКодировки) и ВывестиСекцию(Запись, ИмяСекции, ЭтоТаблица = ложь, х_раз = 1) формируем результирующий документ (Это происходит очень быстро в памяти в структуре «Дерево»). В методе ВывестиСекцию параметр ЭтоТаблица уточняет обычная это секция или секция формирующая таблицу.
-
Правила вывода секций. Перед выводом секции необходимо заполнить параметры внутри ее тела (если это после, то вместо параметров будут пробелы). Сначала необходимо выводить вложенную секцию, после уровнем выше и т.д. до корневой.
-
После формирования документа нужно вызвать метод УдалитьСекции(Запись), Запись — это структура (с 2-мя параметрами «Дерево» и «Секции») возвтращаемая методом РаспарситьСтроку(Стр, ХешКодировки).
-
Последний шаг — функция СформироватьСтроку(Дерево). Возвращает результирующий документ в виде строки. Запиав строку в фаил RTF получим документ RTF.
Еще один момент. В зависимости от того в каком редакторе вы создавали RTF-фаил, внутренний текст может сильно различаться. Я писал код, тестировал и отлаживался на MS Word 2010-м. Правда я не думаю это на что-то может повлиять, но все же решил вас предупредить.
3. Код методов
Почему-то весь текст не влазит, разделил на 3 части.
Код методов из общего модуля:
Первая часть:
Процедура Печать(Элем, Данные) Экспорт
Перем Обработка, Спр, Двоичка, Темп;
Если ТипЗнч(Элем) = Тип("Строка") Тогда
Спр = Справочники.R_ПечатьМакетовRTF.НайтиПоНаименованию(Элем);
Если НЕ ЗначениеЗаполнено(Спр) Тогда
ВызватьИсключение "Элемент справочника " + Элем + " не найден";
КонецЕсли;
Иначе
Спр = Элем;
КонецЕсли;
Двоичка = Спр.Обработка.Получить();
Макет = Спр.Макет.Получить();
Если НЕ ЗначениеЗаполнено(Двоичка) Тогда
ВызватьИсключение "Обработка не выбрана";
КонецЕсли;
Темп = ПолучитьИмяВременногоФайла();
Двоичка.Записать(Темп);
Обработка = ВнешниеОбработки.Создать(Темп);
Обработка.Печать(Макет, Данные);
УдалитьФайлы(Темп);
КонецПроцедуры
Функция СформироватьСтроку(Дерево) Экспорт
Перем рет, Элем;
рет = ""; Стек = Новый Массив;
Для каждого Элем из Дерево Цикл
Если Элем.Тип = "Блок" Тогда
рет = рет + "{" + СформироватьСтроку(Элем.Дерево) + "}";
Иначе
рет = рет + Элем.Текст;
КонецЕсли;
КонецЦикла;
Возврат рет;
КонецФункции
Функция РаспарситьСтроку(Стр, ХешКодировки) Экспорт
Перем Дерево, Стек, Мас, Анализ, Секции, НачатоСлово, БылСлеш, Параметр, Секция, Темп, Блок, СтруктураСекций;
Секции = Новый Массив;
Стек = Новый Массив;
Дерево = Новый Массив;
Мас = Дерево;
НачатоСлово = Ложь;
БылСлеш = Ложь;
Параметр = Неопределено;
Секция = Неопределено;
Темп = "";
Блок = Новый Структура("Дерево, Родитель");
Для х = 1 по СтрДлина(Стр) Цикл
Символ = Сред(Стр, х, 1);
Если НачатоСлово Тогда
Если Символ = " " Тогда
НачатоСлово = Ложь;
Темп = Темп + Символ;
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Темп = "";
Если КомпоновкаКлючевыхСлов(Секции, Анализ, Секция, Параметр) Тогда
Мас.Добавить(Анализ);
КонецЕсли;
ПоискКлючевыхСлов(Секции, Анализ, Секция, Параметр);
Продолжить;
КонецЕсли;
Если Символ = "{" Тогда
Если БылСлеш Тогда
Темп = Темп + Символ;
Иначе
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Если КомпоновкаКлючевыхСлов(Секции, Анализ, Секция, Параметр) Тогда
Мас.Добавить(Анализ);
КонецЕсли;
ПоискКлючевыхСлов(Секции, Анализ, Секция, Параметр);
Темп = "";
НачатоСлово = Ложь;
БылСлеш = Ложь;
Блок = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, ?(Стек.Количество() = 0, Неопределено, Стек[Стек.ВГраница()]));
Если КомпоновкаКлючевыхСлов(Секции, Блок, Секция, Параметр) Тогда
Стек.Добавить(Блок);
Мас.Добавить(Блок);
Мас = Блок.Дерево;
КонецЕсли;
Продолжить;
КонецЕсли;
КонецЕсли;
Если Символ = "}" Тогда
Если БылСлеш Тогда
Темп = Темп + Символ;
Иначе
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Если КомпоновкаКлючевыхСлов(Секции, Анализ, Секция, Параметр) Тогда
Мас.Добавить(Анализ);
КонецЕсли;
ПоискКлючевыхСлов(Секции, Анализ, Секция, Параметр);
Темп = "";
НачатоСлово = Ложь;
БылСлеш = Ложь;
Если КомпоновкаКлючевыхСлов(Секции, Блок, Секция, Параметр) Тогда
Стек.Удалить(Стек.ВГраница());
Если Стек.Количество() = 0 Тогда
Мас = Дерево;
Иначе
Мас = Стек[Стек.ВГраница()].Дерево;
КонецЕсли;
КонецЕсли;
Продолжить;
КонецЕсли;
КонецЕсли;
Если Символ = "" Тогда
Если БылСлеш Тогда
БылСлеш = Ложь;
Иначе
БылСлеш = Истина;
КонецЕсли;
Иначе
Если БылСлеш и Символ <> "u" и Символ <> "'" Тогда
Если Лев(Темп, 2) = "'" или Лев(Темп, 2) = "u" или Лев(Темп, 1) <> "" Тогда
Анализ = АнализСлова(Блок, Лев(Темп, СтрДлина(Темп) - 1), ХешКодировки);
Темп = "";
Если КомпоновкаКлючевыхСлов(Секции, Анализ, Секция, Параметр) Тогда
Мас.Добавить(Анализ);
КонецЕсли;
ПоискКлючевыхСлов(Секции, Анализ, Секция, Параметр);
КонецЕсли;
КонецЕсли;
БылСлеш = Ложь;
КонецЕсли;
Темп = Темп + Символ;
Иначе
Если Символ = "{" Тогда
Блок = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, ?(Стек.Количество() = 0, Неопределено, Стек[Стек.ВГраница()]));
Если КомпоновкаКлючевыхСлов(Секции, Блок, Секция, Параметр) Тогда
Стек.Добавить(Блок);
Мас.Добавить(Блок);
Мас = Блок.Дерево;
КонецЕсли;
Продолжить;
КонецЕсли;
Если Символ = "}" Тогда
Если КомпоновкаКлючевыхСлов(Секции, Блок, Секция, Параметр) Тогда
Стек.Удалить(Стек.ВГраница());
Если Стек.Количество() = 0 Тогда
Мас = Дерево;
Иначе
Мас = Стек[Стек.ВГраница()].Дерево;
КонецЕсли;
КонецЕсли;
Продолжить;
КонецЕсли;
Темп = Символ;
НачатоСлово = Истина;
Если Символ = "" Тогда
БылСлеш = Истина;
Иначе
БылСлеш = Ложь;
КонецЕсли;
КонецЕсли;
КонецЦикла;
СтруктураСекций = СформироватьСтруктуруСекций(Дерево, Секции);
Возврат Новый Структура("Дерево, Секции", Дерево, СтруктураСекций);
КонецФункции
Функция ПолучитьХешКодировки() Экспорт
Перем Хеш;
Хеш = Новый Соответствие;
Хеш["'c0"] = "А";
Хеш["'c1"] = "Б";
Хеш["'c2"] = "В";
Хеш["'c3"] = "Г";
Хеш["'c4"] = "Д";
Хеш["'c5"] = "Е";
Хеш["'c6"] = "Ж";
Хеш["'c7"] = "З";
Хеш["'c8"] = "И";
Хеш["'c9"] = "Й";
Хеш["'ca"] = "К";
Хеш["'cb"] = "Л";
Хеш["'cc"] = "М";
Хеш["'cd"] = "Н";
Хеш["'ce"] = "О";
Хеш["'cf"] = "П";
Хеш["'d0"] = "Р";
Хеш["'d1"] = "С";
Хеш["'d2"] = "Т";
Хеш["'d3"] = "У";
Хеш["'d4"] = "Ф";
Хеш["'d5"] = "Х";
Хеш["'d6"] = "Ц";
Хеш["'d7"] = "Ч";
Хеш["'d8"] = "Ш";
Хеш["'d9"] = "Щ";
Хеш["'da"] = "Ъ";
Хеш["'db"] = "Ы";
Хеш["'dc"] = "Ь";
Хеш["'dd"] = "Э";
Хеш["'de"] = "Ю";
Хеш["'df"] = "Я";
Хеш["'e0"] = "а";
Хеш["'e1"] = "б";
Хеш["'e2"] = "в";
Хеш["'e3"] = "г";
Хеш["'e4"] = "д";
Хеш["'e5"] = "е";
Хеш["'e6"] = "ж";
Хеш["'e7"] = "з";
Хеш["'e8"] = "и";
Хеш["'e9"] = "й";
Хеш["'ea"] = "к";
Хеш["'eb"] = "л";
Хеш["'ec"] = "м";
Хеш["'ed"] = "н";
Хеш["'ee"] = "о";
Хеш["'ef"] = "п";
Хеш["'f0"] = "р";
Хеш["'f1"] = "с";
Хеш["'f2"] = "т";
Хеш["'f3"] = "у";
Хеш["'f4"] = "ф";
Хеш["'f5"] = "х";
Хеш["'f6"] = "ц";
Хеш["'f7"] = "ч";
Хеш["'f8"] = "ш";
Хеш["'f9"] = "щ";
Хеш["'fa"] = "ъ";
Хеш["'fb"] = "ы";
Хеш["'fc"] = "ь";
Хеш["'fd"] = "э";
Хеш["'fe"] = "ю";
Хеш["'ff"] = "я";
Хеш["u1040"] = "А";
Хеш["u1041"] = "Б";
Хеш["u1042"] = "В";
Хеш["u1043"] = "Г";
Хеш["u1044"] = "Д";
Хеш["u1045"] = "Е";
Хеш["u1046"] = "Ж";
Хеш["u1047"] = "З";
Хеш["u1048"] = "И";
Хеш["u1049"] = "Й";
Хеш["u1050"] = "К";
Хеш["u1051"] = "Л";
Хеш["u1052"] = "М";
Хеш["u1053"] = "Н";
Хеш["u1054"] = "О";
Хеш["u1055"] = "П";
Хеш["u1056"] = "Р";
Хеш["u1057"] = "С";
Хеш["u1058"] = "Т";
Хеш["u1059"] = "У";
Хеш["u1060"] = "Ф";
Хеш["u1061"] = "Х";
Хеш["u1062"] = "Ц";
Хеш["u1063"] = "Ч";
Хеш["u1064"] = "Ш";
Хеш["u1065"] = "Щ";
Хеш["u1066"] = "Ъ";
Хеш["u1067"] = "Ы";
Хеш["u1068"] = "Ь";
Хеш["u1069"] = "Э";
Хеш["u1070"] = "Ю";
Хеш["u1071"] = "Я";
Хеш["u1072"] = "а";
Хеш["u1073"] = "б";
Хеш["u1074"] = "в";
Хеш["u1075"] = "г";
Хеш["u1076"] = "д";
Хеш["u1077"] = "е";
Хеш["u1078"] = "ж";
Хеш["u1079"] = "з";
Хеш["u1080"] = "и";
Хеш["u1081"] = "й";
Хеш["u1082"] = "к";
Хеш["u1083"] = "л";
Хеш["u1084"] = "м";
Хеш["u1085"] = "н";
Хеш["u1086"] = "о";
Хеш["u1087"] = "п";
Хеш["u1088"] = "р";
Хеш["u1089"] = "с";
Хеш["u1090"] = "т";
Хеш["u1091"] = "у";
Хеш["u1092"] = "ф";
Хеш["u1093"] = "х";
Хеш["u1094"] = "ц";
Хеш["u1095"] = "ч";
Хеш["u1096"] = "ш";
Хеш["u1097"] = "щ";
Хеш["u1098"] = "ъ";
Хеш["u1099"] = "ы";
Хеш["u1100"] = "ь";
Хеш["u1101"] = "э";
Хеш["u1102"] = "ю";
Хеш["u1103"] = "я";
Хеш["А"] = "'c0";
Хеш["Б"] = "'c1";
Хеш["В"] = "'c2";
Хеш["Г"] = "'c3";
Хеш["Д"] = "'c4";
Хеш["Е"] = "'c5";
Хеш["Ж"] = "'c6";
Хеш["З"] = "'c7";
Хеш["И"] = "'c8";
Хеш["Й"] = "'c9";
Хеш["К"] = "'ca";
Хеш["Л"] = "'cb";
Хеш["М"] = "'cc";
Хеш["Н"] = "'cd";
Хеш["О"] = "'ce";
Хеш["П"] = "'cf";
Хеш["Р"] = "'d0";
Хеш["С"] = "'d1";
Хеш["Т"] = "'d2";
Хеш["У"] = "'d3";
Хеш["Ф"] = "'d4";
Хеш["Х"] = "'d5";
Хеш["Ц"] = "'d6";
Хеш["Ч"] = "'d7";
Хеш["Ш"] = "'d8";
Хеш["Щ"] = "'d9";
Хеш["Ъ"] = "'da";
Хеш["Ы"] = "'db";
Хеш["Ь"] = "'dc";
Хеш["Э"] = "'dd";
Хеш["Ю"] = "'de";
Хеш["Я"] = "'df";
Хеш["а"] = "'e0";
Хеш["б"] = "'e1";
Хеш["в"] = "'e2";
Хеш["г"] = "'e3";
Хеш["д"] = "'e4";
Хеш["е"] = "'e5";
Хеш["ж"] = "'e6";
Хеш["з"] = "'e7";
Хеш["и"] = "'e8";
Хеш["й"] = "'e9";
Хеш["к"] = "'ea";
Хеш["л"] = "'eb";
Хеш["м"] = "'ec";
Хеш["н"] = "'ed";
Хеш["о"] = "'ee";
Хеш["п"] = "'ef";
Хеш["р"] = "'f0";
Хеш["с"] = "'f1";
Хеш["т"] = "'f2";
Хеш["у"] = "'f3";
Хеш["ф"] = "'f4";
Хеш["х"] = "'f5";
Хеш["ц"] = "'f6";
Хеш["ч"] = "'f7";
Хеш["ш"] = "'f8";
Хеш["щ"] = "'f9";
Хеш["ъ"] = "'fa";
Хеш["ы"] = "'fb";
Хеш["ь"] = "'fc";
Хеш["э"] = "'fd";
Хеш["ю"] = "'fe";
Хеш["я"] = "'ff";
Возврат Хеш;
КонецФункции
Процедура ЗаполнитьПараметр(СтруктураСекций, ИмяПараметра, ЗначениеЗаполнения, ХешКодировки) Экспорт
Перем х, Темп, Символ, Элем, Параметр, Значение;
Параметр = СтруктураСекций.Параметры[ИмяПараметра];
Если НЕ ЗначениеЗаполнено(Параметр) Тогда
ВызватьИсключение "Не существует параметра с именем " + ИмяПараметра;
КонецЕсли;
Значение = Строка(ЗначениеЗаполнения);
Темп = "";
Для х = 1 по СтрДлина(Значение) Цикл
Символ = Сред(Значение, х, 1);
Если Символ = "" Тогда
Темп = Темп + "\";
Продолжить;
КонецЕсли;
Если Символ = "{" Тогда
Темп = Темп + "{";
Продолжить;
КонецЕсли;
Если Символ = "}" Тогда
Темп = Темп + "}";
Продолжить;
КонецЕсли;
ХешСимвол = ХешКодировки[Символ];
Темп = ?(ЗначениеЗаполнено(ХешСимвол), Темп + ХешСимвол, Темп + Символ);
КонецЦикла;
Для каждого Элем из Параметр Цикл
Элем.Текст = Темп;
КонецЦикла;
КонецПроцедуры
Вторая часть:
Процедура ВывестиСекцию(Запись, ИмяСекции, ЭтоТаблица = ложь, х_раз = 1) Экспорт
//Можно конечно в цикле вызывать метод ВывестиСекцию(Дерево, МасСекций)
//Но лучше использовать параметр "х_раз", это ускорит работу
Перем у, х, Мас, ИндексНачала, ИндексКонца, Элем, Секция, ТемпМас, раз, ХешСекций, МасСекций;
Перем Дерево, СтруктураСекций, НачалоСекции;
Дерево = Запись.Дерево;
СтруктураСекций = Запись.Секции;
ХешСекций = СтруктураСекций.ХешСекций;
МасСекций = СтруктураСекций.Секции[ИмяСекции];
Если НЕ ЗначениеЗаполнено(МасСекций) Тогда
ВызватьИсключение "Не найдена секция с именем " + ИмяСекции;
КонецЕсли;
у = МасСекций.Вграница();
Пока у >= 0 Цикл
Секция = МасСекций[у];
ИндексНачала = -1;
ИндексКонца = -1;
Мас = ?(ЗначениеЗаполнено(Секция.БлокНачала.Родитель), Секция.БлокНачала.Родитель.Дерево, Дерево);
Для х = 0 по Мас.Вграница() Цикл
Если Мас[х] = Секция.БлокНачала Тогда
ИндексНачала = х;
Для х = ИндексНачала по Мас.Вграница() Цикл
Если Мас[х] = Секция.БлокКонца Тогда
ИндексКонца = х;
Прервать;
КонецЕсли;
КонецЦикла;
Прервать;
КонецЕсли;
КонецЦикла;
ТемпМас = Новый Массив;
НачалоСекции = Ложь;
Для х = ИндексНачала + 1 + ?(ЭтоТаблица, 1, 0) по ИндексКонца - 1 Цикл
Если НачалоСекции Тогда
Если ХешСекций[Мас[х]] = "Конец" Тогда
НачалоСекции = Ложь;
Продолжить;
КонецЕсли;
Иначе
Если ХешСекций[Мас[х]] = "Начало" Тогда
НачалоСекции = Истина;
Продолжить;
Иначе
ТемпМас.Добавить(Мас[х]);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Для раз = 1 по х_раз Цикл
х = ТемпМас.ВГраница();
Пока х >= 0 Цикл
Элем = СкопироватьЭлементДерева(ТемпМас[х], ТемпМас[х].Родитель);
Мас.Вставить(ИндексНачала, Элем);
х = х - 1;
КонецЦикла;
КонецЦикла;
у = у - 1;
КонецЦикла;
КонецПроцедуры
Процедура УдалитьСекции(Запись) Экспорт
Перем Мас, Секция, НачатоУдаление, х, Элем, МасСекций, Дерево, СтруктураСекций;
Дерево = Запись.Дерево;
СтруктураСекций = Запись.Секции;
Для Каждого Элем из СтруктураСекций.Секции Цикл
МасСекций = Элем.Значение;
у = МасСекций.Вграница();
Пока у >= 0 Цикл
Секция = МасСекций[у];
Мас = ?(ЗначениеЗаполнено(Секция.БлокНачала.Родитель), Секция.БлокНачала.Родитель.Дерево, Дерево);
х = 0;
НачатоУдаление = Ложь;
Пока х 0 Тогда
Если Найти(Элем.Текст, ">") > 0 Тогда
ДобавлениеСекции(Секции, Элем, "Секция");
Иначе
Секция = Элем;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(Секция) Тогда
Если (Элем.Тип = "Текст") Тогда
Если Найти(Элем.Текст, "[") > 0 Тогда
Если Найти(Элем.Текст, "]") > 0 Тогда
ДобавлениеСекции(Секции, Элем, "Параметр");
Иначе
Параметр = Элем;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция КомпоновкаКлючевыхСлов(Секции, Элем, Секция, Параметр)
Перем рет;
рет = Истина;
Если ЗначениеЗаполнено(Секция) Тогда
рет = Ложь;
Если Элем.Тип = "Текст" Тогда
Секция.Текст = Секция.Текст + Элем.Текст;
Секция.Значение = Секция.Значение + Элем.Значение;
Если Найти(Элем.Текст, ">") > 0 Тогда
ДобавлениеСекции(Секции, Секция, "Секция");
Секция = Неопределено;
КонецЕсли;
КонецЕсли;
Иначе
Если ЗначениеЗаполнено(Параметр) Тогда
рет = Ложь;
Если Элем.Тип = "Текст" Тогда
Параметр.Текст = Параметр.Текст + Элем.Текст;
Параметр.Значение = Параметр.Значение + Элем.Значение;
Если Найти(Элем.Текст, "]") > 0 Тогда
ДобавлениеСекции(Секции, Параметр, "Параметр");
Параметр = Неопределено;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Возврат рет;
КонецФункции
Процедура ДобавлениеСекции(Секции, Секция, Тип = "Параметр")
Секции.Добавить(Новый Структура("Секция, Тип", Секция, Тип));
КонецПроцедуры
Функция АнализСлова(Блок, Стр, ХешКодировки)
Перем рет, х, Символ, БылСлеш, у, Значение, Темп, ЗначениеКодировки;
ЭтоТекст = Ложь;
Символ = Сред(Стр, 1, 1);
Если КодСимвола(Символ, 1) = 13 Тогда
Символ = Сред(Стр, 2, 1);
Если Символ = "" Тогда
Символ = Сред(Стр, 3, 1);
Если Символ = "'" или Символ = "u" Тогда
ЭтоТекст = Истина;
КонецЕсли;
Иначе
Если КодСимвола(Символ, 1) = 10 Тогда
Символ = Сред(Стр, 3, 1);
Если Символ = "" Тогда
Символ = Сред(Стр, 4, 1);
Если Символ = "'" или Символ = "u" Тогда
ЭтоТекст = Истина;
КонецЕсли;
Иначе
ЭтоТекст = Истина;
КонецЕсли;
Иначе
Если Символ = "" Тогда
Символ = Сред(Стр, 2, 1);
Если Символ = "'" или Символ = "u" Тогда
ЭтоТекст = Истина;
КонецЕсли;
Иначе
ЭтоТекст = Истина;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Иначе
Если Символ = "" Тогда
Символ = Сред(Стр, 2, 1);
Если Символ = "'" или Символ = "u" Тогда
ЭтоТекст = Истина;
КонецЕсли;
Иначе
ЭтоТекст = Истина;
КонецЕсли;
КонецЕсли;
Если ЭтоТекст Тогда
у = 0; Темп = ""; БылСлеш = Ложь; Значение = "";
Для х = 1 по СтрДлина(Стр) Цикл
Символ = Сред(Стр, х, 1);
Если БылСлеш Тогда
Если Символ = "" или Символ = "{" или Символ = "}" Тогда
Значение = Значение + Символ;
БылСлеш = Ложь;
Продолжить;
КонецЕсли;
Если Символ = "'" Тогда
Темп = "'";
у = 2;
БылСлеш = Ложь;
Продолжить;
КонецЕсли;
Если Символ = "u" Тогда
Темп = "u";
у = 4;
БылСлеш = Ложь;
Продолжить;
КонецЕсли;
Иначе
Если у > 0 Тогда
у = у - 1;
Темп = Темп + Символ;
Если у = 0 Тогда
ЗначениеКодировки = ХешКодировки[Темп];
Если ЗначениеЗаполнено(ЗначениеКодировки) Тогда
Значение = Значение + ЗначениеКодировки;
КонецЕсли;
КонецЕсли;
Продолжить;
КонецЕсли;
Если Символ = "" Тогда
БылСлеш = Истина;
Продолжить;
КонецЕсли;
Значение = Значение + Символ;
КонецЕсли;
КонецЦикла;
рет = Новый Структура("Тип, Текст, Значение, Родитель", "Текст", Стр, Значение, Блок);
Иначе
рет = Новый Структура("Тип, Текст, Родитель", "Команда", Стр, Блок);
КонецЕсли;
Возврат рет;
КонецФункции
Третья часть:
Функция СформироватьСтруктуруСекций(Дерево, Секции)
Перем рет, Темп, Запись, Мас, ТемпМас;
рет = новый Структура("Секции, Параметры, ХешСекций");
рет.Секции = Новый Соответствие;
рет.Параметры = Новый Соответствие;
рет.ХешСекций = Новый Соответствие;
Для каждого Элем из Секции Цикл
Если Элем.Тип = "Параметр" Тогда
Темп = Элем.Секция.Значение;
Темп = ОчиститьНазвание(Темп);
Если рет.Параметры[Темп] = Неопределено Тогда
ТемпМас = Новый Массив;
ТемпМас.Добавить(Элем.Секция);
рет.Параметры[Темп] = ТемпМас;
Иначе
рет.Параметры[Темп].Добавить(Элем.Секция);
КонецЕсли;
Элем.Секция.Текст = "";
Продолжить;
КонецЕсли;
Если Элем.Тип = "Секция" Тогда
Запись = ОчиститьСекцию(Дерево, Элем);
Если рет.Секции[Запись.Название] = Неопределено Тогда
Если Запись.ТипСекции = "Конец" Тогда
ВызватьИсключение "Конец секции не может располагаться до ее начала";
КонецЕсли;
Темп = Новый Структура("Дерево, БлокНачала, БлокКонца", Запись.Дерево, Запись.БлокСекции, Неопределено);
ТемпМас = Новый Массив;
ТемпМас.Добавить(Темп);
рет.Секции[Запись.Название] = ТемпМас;
Иначе
Если Запись.ТипСекции = "Начало" Тогда
Темп = Новый Структура("Дерево, БлокНачала, БлокКонца", Запись.Дерево, Запись.БлокСекции, Неопределено);
рет.Секции[Запись.Название].Добавить(Темп);
Иначе
Темп = рет.Секции[Запись.Название];
х = Темп.Вграница();
Пока х >= 0 Цикл
Если Темп[х].БлокКонца = Неопределено Тогда
ПоискКонцаСекции(Дерево, Запись, Темп[х]);
Прервать;
КонецЕсли;
х = х - 1;
КонецЦикла;
КонецЕсли;
КонецЕсли;
рет.ХешСекций[Запись.БлокСекции] = Запись.ТипСекции;
КонецЕсли;
КонецЦикла;
Возврат рет;
КонецФункции
Процедура ПоискКонцаСекции(Дерево, ЗаготовкаСекции, ЭлементСекции)
Перем Блок, Элем, ПоследнийБлок;
Блок = ЗаготовкаСекции.БлокСекции;
Пока ЗначениеЗаполнено(Блок) Цикл
Если ЗаготовкаСекции.Дерево = ЭлементСекции.Дерево Тогда
ЭлементСекции.БлокКонца = ЗаготовкаСекции.БлокСекции;
Прервать;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(Блок.Родитель) Тогда
БлокСекции = Блок;
КонецЕсли;
Блок = Блок.Родитель;
КонецЦикла;
Если НЕ ЗначениеЗаполнено(ЭлементСекции.БлокКонца) Тогда
Если ЗаготовкаСекции.Дерево = Дерево Тогда
Для каждого Элем из Дерево Цикл
Если Элем = БлокСекции Тогда
ЭлементСекции.БлокКонца = Элем;
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(ЭлементСекции.БлокКонца) Тогда
ЭлементСекции.БлокКонца = ЭлементСекции.БлокНачала;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция ОчиститьНазвание(ОчищаемоеНазвание)
Перем рет, х, Символ, Название;
рет = ""; Название = СокрЛП(ОчищаемоеНазвание);
Для х = 1 по СтрДлина(Название) Цикл
Символ = Сред(Название, х, 1);
Если Символ = "]" Тогда
Прервать;
Иначе
Если НЕ (КодСимвола(Символ, 1) = 13 или КодСимвола(Символ, 1) = 10 или Символ = "[") Тогда
рет = рет + Символ;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат рет;
КонецФункции
Функция СкопироватьЭлементДерева(Элем, Родитель)
Перем х, рет, НовЭлем;
Если Элем.Тип = "Блок" Тогда
рет = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, Родитель);
Для х = 0 по Элем.Дерево.ВГраница() Цикл
НовЭлем = СкопироватьЭлементДерева(Элем.Дерево[х], рет);
рет.Дерево.Добавить(НовЭлем);
КонецЦикла;
ИначеЕсли Элем.Тип = "Текст" Тогда
рет = Новый Структура("Тип, Текст, Значение, Родитель", "Текст", Элем.Текст, Элем.Значение, Элем.Родитель);
ИначеЕсли Элем.Тип = "Команда" Тогда
рет = Новый Структура("Тип, Текст, Родитель", "Команда", Элем.Текст, Элем.Родитель);
Иначе
ВызватьИсключение "Не ожиданный элемент тип = " + Элем.Тип;
КонецЕсли;
Возврат рет;
КонецФункции
Функция ОчиститьСекцию(Дерево, Секция)
Перем рет, Темп, х;
рет = Новый Структура(«Дерево, БлокСекции, Название, ТипСекции»);
Темп = ОчиститьНазвание(СокрЛП(Секция.Секция.Значение));
Если Лев(Темп, 2) = «</» Тогда
рет.ТипСекции = «Конец»;
рет.Название = Сред(Темп, 3, СтрДлина(Темп) — 3);
ИначеЕсли Лев(Темп, 1) = «<» Тогда
рет.ТипСекции = «Начало»;
рет.Название = Сред(Темп, 2, СтрДлина(Темп) — 2);
Иначе
ВызватьИсключение «Невозможно определить тип секции»;
КонецЕсли;
рет.Название = ОчиститьНазвание(рет.Название);
Секция.Секция.Текст = «»;
Темп = Секция.Секция.Родитель.Родитель;
рет.БлокСекции = Секция.Секция.Родитель;
рет.Дерево = ?(ЗначениеЗаполнено(Темп), Темп.Дерево, Дерево);
Возврат рет;
КонецФункции
Автор, пожалей читателей, исправь ошибки, читать же невозможно!
Кстати, а никто не баловался xslt-преобразованием в rtf-формат? Так, чтобы ррраз — и по прописанному готовенькое?
(1) Yashazz, Я безжалостен 🙂
К несчастью метод
нормально не оформляется из-за текста = «< / »
Поэтому добавил его в виде обычного текста.
Понимаю, что смотреть что-то чужое, когда свое работает — не хочется. Но я вот вот с разбором rtf тоже проходил, и вроде достаточно сложные отчеты строились.
http://infostart.ru/public/71345/
(4) K_A_O, А вы COM объекты используете ?
Я специально не использовал, что бы под Linux работало.
(5) Разбирается rtf файл как строка, правда с помощью регулярных выражений, но если использовать нативную компоненту, то будет работать везде. Разбор ведется так же через соответствие представления «u1040 = А» и т.д.
А что аналога ЗапуститьПриложение нет для Линукс?
Добрый день ! Есть ли у вас вариант для управляемого приложения ?