Добрый день.

Подскажите, пожалуйста, наиболее удобный способ конвертации из DOC/DOCX в RTF и обратно.

Желательно без дополнительного подключения внешних библиотек.

Спасибо!

Нравится

2 комментария
Лучший ответ

Без внешних сборок навряд ли получится разве что дергать онлайн сервисы наподобии google docs или microsoft office online. 

Без внешних сборок навряд ли получится разве что дергать онлайн сервисы наподобии google docs или microsoft office online. 

Григорий Чех,

Понятно. Спасибо.

Показать все комментарии

Добрый день.

Подскажите, как можно реализовать такую функциональность:

Я создал подраздел "Заметки", , который состоит из названия и текст заметки в формате RTF (отображается в виде RichDataControl)

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

Я подключил поле типа BLOB (RichDataControl). Если я ввожу ее в карточке, то работает ввод, но как это сделать в реестре - не могу найти. В самом реестре введенное значение сохраняется, но теряется после ввода.

Я сделал это через MemoControl - т.е запись в MemoControl.Value при событии DataGridOnSelectionChange в реестре и запись в Dataset при событии mmNoteBodyOnExit в MemoControl, такой подход работает. Но после замены MemoControl на RichDataControl запись в свойство Value выдает ошибку "Обьект не поддерживает это свойство или метод"

Прошу вашей помощи

Нравится

1 комментарий

Дмитрий, почему не использовать для внесения и отображения текста заметок отдельный WindowContainer с окном wnd_Description (аналогично детали [Описание]). Во вложении прилагаю набор модифицированных сервисов. Ознакомьтесь с вариантом решения.

Показать все комментарии

Добрый день.
Уважаемое сообщество, помогите разобраться с такой задачей.

Terrasoft CRM 3.3.2
У нас в поле "Описание" таблицы контрагентов хранятся записи вида:

** горизонтальная линия **
Иванов Иван Иванович - 1 апреля 2010 г. 12:00:00
Клиент не оплатил счет

** горизонтальная линия **
Иванов Иван Иванович - 5 апреля 2010 г. 15:00:00
телефон клиента не отвечает

** горизонтальная линия **
Петров Петр Петрович - 12 апреля 2010 г. 14:15:25
клиент для нас потерян

, где ** горизонтальная линия ** - это нарисованная коричневая полоска, не текст.

Поступила задача реализовать удаление дублей таким образом, чтобы все "описания" дублей сливались в итогового контрагента с сортировкой по датам.

Я вижу такую последовательность действий: слить все описания во временный объект, в нем отсортировать и сохранить результат в "Описании" результирующего контрагента.

Из вышенаписанного вытекает нижеследующее:
1. Возможно ли из RTF-текста выдирать строки (желательно в виде текста, а не набора символов Unicode, в котором по-умолчанию хранятся кирилические символы) в какой-либо буферный объект?

2. Возможно ли проходом по RTF-тексту получить положение этой коричневой полоски, чтобы использовать ее как разделитель записей?

Есть ли какие возможности реализовать это?

Нравится

10 комментариев

Здравствуйте, Ерошенко Петр Семенович!

Алгоритм реализации данной задачи я вижу следующим:

1. Получить блок данных детали "Описание" и дату добавления этих данных;
2. Внести полученные на предыдущем шаге данные (в формате RTF), дату (в формате БД сервера), и контрагент в предварительно созданную таблицу с соответствующими полями (MESSAGE, MESSAGEDATE, ACCOUNT). При этом, необходимо преобразовать дату с формата RTF в тип данных "дата/время".
3. В созданной таблице отсортировать записи по messageDate, и вставить на деталь "Описание" (поле Description) все записи из колонки Message.

Вот пример кода который получает отдельные записи и дату их внесения:

function RTF()
{ 
	// Dataset = ds_Description
	var desc = 	Dataset.ValAsStr('Description');       
	getMessage(desc);	
        getMessageDate(desc);	
}
 
function getMessage(desc)
{    	
	var Message = "";
	var startIndex = 0;
	var endIndex = 0;
	// split into an array 
	var array = desc.split('\n');	
	for(var i = 0; i < array.length; i++)
	{
		if(array[i].indexOf("brdrs") != -1 && startIndex != 0)
		{
			 endIndex = i;
			 for(var a = startIndex; a < endIndex; a++)
			 {
			 	Message += array[a];
			 }   		
			 postToDB(Message);                        
			 // clear Message and Indexes
			 Message = "";
			 startIndex = 0;
			 endIndex = 0; 
		}
		if(array[i].indexOf("brdrs") != -1 && startIndex == 0)
		{
			startIndex = i;
		}
		// for last message
		if(array[i].indexOf("par}") != -1)
		{
			endIndex = i;
			for(var b = startIndex; b <= endIndex; b++)
			{
				Message += array[b];
			}
			postToDB(Message);                        
		}
	}
}
function getMessageDate(desc)
{   
	var messageDate = "";
	var array = desc.split('\n');
	for(var i = 0; i < array.length; i++)
	{
		if(array[i].indexOf("cs6") != -1)
		{
			for (var a = 0; a < array[i].length; a++)
			{
				if(array[i][a] == '-')
				{
					for(var b = a + 2; b < array[i].length; b++)
					{
						messageDate += array[i][b];
					} 
					postToDB(messageDate);					
					messageDate = "";
				}
			}
		}
	}
 
}
function postToDB(Message)
{
	//CODE FOR POSTING MESSAGE AND MESSAGEDATE TO DATABASE HERE
}

Спасибо за пример. А что дает поиск подстроки "cs6" в этом фрагменте? В моем случае она не встречается.

for(var i = 0; i < array.length; i++)
         {
                 if(array[i].indexOf("cs6") != -1)
                 {
                         for (var a = 0; a < array[i].length; a++)
                         {

Здравствуйте, Петр Семенович.

После символа "-" в RTF документе фиксируется дата создания сообщения. Однако данный символ может использоваться пользователем и в самом тексте сообщения. Поэтому, по определенным условиям необходимо идентифицировать строку, которая содержит в себе дату. В моем примере подстрока "cs6" предоставляет такую возможность:


\par \plain \cs6\f0\i\fs20\cf13 \u1045 \'c5\u1074 \'e2\u1075 \'e3\u1077 \'e5\u1085 \'ed\u1080 \'e8\u1081 \'e9\plain \f0\i\fs20\cf13 \plain \cs6\f0\i\fs20\cf13 \u1052 \'cc\u1080 \'e8\u1088 \'f0\u1085 \'ed\u1099 \'fb\u1081 \'e9\plain \f0\i\fs20\cf13 - 3 \plain \cs6\f0\i\fs20\cf13 \u1092 \'f4\u1077 \'e5\u1074 \'e2\u1088 \'f0\u1072 \'e0\u1083 \'eb\u1103 \'ff\plain \f0\i\fs20\cf13 2012 \plain \cs6\f0\i\fs20\cf13 \u1075 \'e3\plain \f0\i\fs20\cf13 . 12:51:12

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

Понятно, спасибо.

Возник еще один вопрос: возможно ли обновить поле "Description" итогового контрагента посредством UpdateQuery?

Здравствуйте Петр!

Обновить поле "Description" для контрагента, посредством UpdateQuery, возможно.
Для этого нужно создать updateQuery сервис, добавить значение Description типа "большой бинарный объект", параметр ID типа уникальный идентификатор, и фильтр сравнения tbl_Accaunt.ID = Parameter: ID.

После чего, запустите созданный сервис на исполнение.
К примеру:

var UpdateQuery = Services.GetNewItemByUsi('uq_AccountDesc');
var Parameters = UpdateQuery.Parameters;
var ID = Dataset.Values('ID');
var Desc = "new description";
SetParameterValue(Parameters, 'ID', ID);
SetParameterValue(Parameters, 'Description', Desc);
UpdateQuery.Execute();

"Олейник Дмитрий" написал:К примеру:

var UpdateQuery = Services.GetNewItemByUsi('uq_AccountDesc');
var Parameters = UpdateQuery.Parameters;
var ID = Dataset.Values('ID');
var Desc = "new description";
 SetParameterValue(Parameters, 'ID', ID);
 SetParameterValue(Parameters, 'Description', Desc);
 UpdateQuery.Execute();


А в там случае строку с RTF-текстом специально приводить к BLOB не нужно как здесь? Если запустить Ваш пример, то в базу запишется пустая строка.

В данном случае приводить строку к BLOB не нужно:

//Dataset = ds_description
var Dataset = DatasetLink1.Dataset;
Dataset.Open();
var DescNew = ' aaaa ';
Dataset.Edit();
Dataset.Values('Description') = DescNew;
Dataset.Post();
var Desc = Dataset.ValAsStr('Description');
MessageBox(Desc);

Дмитрий, попробовал запустить ваш пример в чистом виде

var UpdateQuery = Services.GetNewItemByUsi('uq_AccountDesc');
var Parameters = UpdateQuery.Parameters;
var ID = Dataset.Values('ID');
var Desc = "new description";
  SetParameterValue(Parameters, 'ID', ID);
  SetParameterValue(Parameters, 'Description', Desc);
  UpdateQuery.Execute();

При его выполнении не происходит записи RTF-текста в поле Description

exec sp_executesql N'UPDATE [dbo].[tbl_Account]
	SET [Description] = @P1,
	[ModifiedOn] = getdate(),
	[ModifiedByID] = ''{C727482F-D8EE-4416-B369-BDC9926E68F5}''
WHERE([tbl_Account].[ID] = @P2)',N'@P1 varbinary(8000),@P2 varchar(8000)',NULL,'{41229A2E-094E-4F28-B84E-316C94A6B522}'

, хотя в коллекцию параметров от записывается (проверил в отладчике). Я поэтому и спрашивал, возможно ли обновлять BLOB-поля через UpdateQuery.

Петр,
действительно если использовать updateQuery необходимо преобразовывать строку в BLOB.
Как вариант, предлагаю использовать способ описанный мной выше:

//dataset = ds_description
var Dataset = DatasetLink1.Dataset;
Dataset.Open();	
var DescNew = 'new value in RTF format ';
Dataset.Edit();
Dataset.Values('Description') = DescNew;
Dataset.Post();

В таком случае преобразование происходит автоматически:

exec sp_executesql N'UPDATE [dbo].[tbl_Account]
	SET [Description] = @P1,
	[ModifiedOn] = getdate(),
	[ModifiedByID] = ''{59B9825D-A16D-48AF-9C20-4DF1100E4BD5}''
WHERE([tbl_Account].[ID] = @P2)',N'@P1 varbinary(8000),@P2 uniqueidentifier',0x6E65772076616C756520696E2052544620666F726D617420,'E308B781-3C5B-4ECB-89EF-5C1ED4DA488E'
Показать все комментарии

Подскажите, как сделать (если это возможно) чтобы отчет созданный в FastReport после его вызова автоматически сконвертировался в RTF.
------------------------
TS CRM 3.0.2.66 firebird

Нравится

3 комментария

Для экспорта есть функция ExportReport(retRTF) из scr_BaseFastReportPreview
retRTF = 0x00000000 - объявлен в scr_SysEnums

Либо в SDK смотрите метод Export для ReportPreviewer

Как-то не совсем понятно,
попробовал сделать так:
function PreviewReport(AWindow) {
ExportReport(retRTF);
frpMain.PreviewReport();
}
, но как я понял в метод Export надо передать полный путь к файлу для сохранения (FileName) и параметр ShowDialog установить в false.
Когда вместо ExportReport(retRTF); пишу
frpMain.Export(0, 'c://temp/test.rtf', false);
получаю ошибку.
Как сделать правильно ?

Дело в том, что второй параметр в Export - это объект, а не просто имя файла.
Посмотрите function SendByEmail(ReportExportType) в scr_BaseFastReportPreviewScript, там как раз вызывают экспорт в конце

Показать все комментарии

если нужно експортировать поле описание в формате rtf нужно только
создать action в окне воркспейса из которого планируэться импорт
и вызвать фн

function amiMyActionOnExecute(Control) {
  ExportFromWnd(Self);
}

+ добавить в используэмые скрипти scr_ExportUtils из атача
все остальное зделает фн

+ простота в использовании
- если в описании есть перевод строки в Excele описание будет разбито на несколько ячеек поетому я задаю смещение на ячейку
но если Вы уверены что у вас нет ентера в описании можете закоментировать строчку 96 DescIndex++;
- нельзя выбрать куда сохранять временные файли
- не удаляються временные файлы

Но ето все временно если решение заинтересуэт продолжу разработку :)

Нравится

Поделиться

0 комментариев
Показать все комментарии

Часто задают вопрос о работе с данными детали "Описание" без визуального компонента. Особенно часто получаю вопрос о переносе строки. И вот решил осветить немного эту тематику.
В детали "Описание" текст представлен в формате RTF и в базу данных он ложится именно в таком виде. А значит, если есть задача видоизменить текст, то нужно понимать, что работа ведется именно с текстом RTF-документа.
А теперь по поводу переноса строки.
Разрыв строки добавляется очень просто. Если не углубляться в дебри описания структуры RTF-документа, то некоторую информацию можно получить, если включить отладку и посмотреть значение поля Description в датасете. Там видно какие управляющие символы используются в том или ином случае. В частности, по переносу строки. Учитывая вышеизложенное, выполним слеюдующее:

1. Добавляем текст в визуальный компонент обычным способом. Я добавил такой текст:
aaa1
bbb1
ccc1
ddd1

2. Включив отладку в детали «Описание», получил следующее значение поля Description:

{\rtf1\ansi\ansicpg0\uc1\deff0\deflang0\deflangfe0{\fonttbl{\f0\fnil Arial;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}

\uc1
\pard\fi0\li0\ql\ri0\sb0\sa0\itap0 \plain \f0\fs20 aaa1
\par bbb1
\par ccc1
\par ddd1\par}

Анализируя текст, несложно заметить:
а. В начале заголовок RTF-документа, он нас сейчас не интересует.
б. Дальше идут инструкции по формату текста, тоже сейчас не нужны.
в. А вот где начинается введенный мною текст, можно увидеть инструкцию \par – это и есть перенос строки. Нужно еще обратить внимание, что инструкция отделяется от текста пробелом, а в конце документа обязательно стоит завершающая фигурная скобка.

Информацию по остальным инструкциям можно поискать в описании формата или методом «научного тыка» через отладку.

Нравится

Поделиться

1 комментарий

Конечно метод "научного тыка" это хорошо. Но как показывает практика - компонент используемый в Террасофт для отображения детали "Описание" поддерживает далеко не все инструкции стандарта rtf.
Полезная ссылка - некоторые инструкции rtf, язык русский.
http://articles.org.ru/docum/rtf.php

Показать все комментарии