Главная » Хабрахабр » Работа с API КОМПАС-3D → Урок 13 → Параграфы

Работа с API КОМПАС-3D → Урок 13 → Параграфы

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

Содержание цикла уроков «Работа с API КОМПАС-3D»

  1. Основы
  2. Оформление чертежа
  3. Корректное подключение к КОМПАС
  4. Основная надпись
  5. Графические примитивы
  6. Сохранение документа в различные форматы
  7. Знакомство с настройками
  8. Более сложные методы записи в основную надпись
  9. Чтение ячеек основной надписи
  10. Спецсимволы, включающие строку
  11. Простые текстовые надписи
  12. Составные строки
  13. Параграфы
  14. Многострочный текст

Параметры параграфа (ksParagraphParam)

Параграф описывается интерфейсом ksParagraphParam. Для его получения нужно использовать метод GetParamStruct интерфейса KompasObject, для этого ему нужно передать константу ko_ParagraphParam (0x0000001B). Рассмотрим свойства интерфейса ksParagraphParam.

Откладывается от горизонтальной линии против часовой стрелки. ang – угол наклона текста в градусах. Аналогичен параметру ang метода ksText.

height – высота параграфа в миллиметрах.

Данное свойство используется, когда текст не умещается в параграф по ширине. hFormat – форматирование текста по горизонтали. Допустимые значения перечислены в таблице ниже.

style – стиль текста (описывался на уроке 11).

Данное свойство используется, когда текст не умещается в параграф по высоте. vFormat – форматирование текста по вертикали. Допустимые значения перечислены в таблице ниже.

При работе со свойством vFormat нужно помнить два момента:

  1. Согласно документации КОМПАС допустимыми значениями свойства vFormat являются значения 0 и -1, но это не так. Допустимые значения: 0 и 1.
  2. КОМПАС не меняет высоту символов. Он изменяет только расстояние между строками. Если высота строк меньше высоты параграфа, то они могут накладываться друг на друга. Пример такого наложения — на рисунке ниже.

Этот метод гораздо надежнее и эффективнее в отличие от метода ksGetTextLength, обсуждавшегося на уроке 11. width – ширина параграфа в миллиметрах.
Свойства height, hFormat, vFormat и width позволяют решать задачу размещения текста в заданном прямоугольнике.

Положение параграфа относительно точки привязки вдоль горизонтальной оси настраивается методом ksSetTextAlign интерфейса ksDocument2D (правда, такая возможность не документирована). x и y – координаты точки привязки. Изменить это поведение нельзя.
Метод у интерфейса ksParagraphParam всего один: Init(). По вертикали точка привязки всегда совпадает с низом первой строки параграфа. Не имеет входных параметров. Он инициализирует значения свойств интерфейса. В случае успеха возвращает значение true.

Построение параграфа

Создание параграфа состоит из трёх последовательных этапов.

  1. Объявление начала параграфа. Для этого вызывается метод ksParagraph интерфейса ksDocument2D. В качестве единственного параметра данный метод принимает интерфейс ksParagraphParam, задающий параметры параграфа. В случае успеха метод ksParagraph возвращает единицу, а в случае ошибки – ноль.
  2. Наполнение параграфа. Для каждой строки, выводимой в параграф, вызывается метод ksTextLine интерфейса ksDocument2D. В качестве единственного параметра он принимает интерфейс ksTextItemParam или ksTextLineParam (рассматривались на предыдущих уроках цикла), описывающие строку. Учтите, выводимые строки не должны содержать символы @, $, &, ~, ^ и #, так как они являются управляющими символами. Работа с ними будет рассмотрена на следующих уроках цикла.
  3. Завершение параграфа. Для этого вызывается метод ksEndObj() интерфейса ksDocument2D. Он не имеет входных параметров и в случае успеха возвращает целочисленный указатель на созданный объект (параграф). В случае ошибки он возвращает ноль.

Пример. Простейший параграф

Ниже приводится исходный код программы, демонстрирующий построение простейшего параграфа.

//Получаем интерфейс ksTextItemParam
TextItemParamPtr textItemParam;
textItemParam = static_cast<TextItemParamPtr>(kompas->GetParamStruct(ko_TextItemParam)); //Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast<ParagraphParamPtr>(kompas->GetParamStruct(ko_ParagraphParam)); paragraphParam->Init();
paragraphParam->set_x(100.0);
paragraphParam->set_y(100.0); //Начинаем параграф
Document2D->ksParagraph(paragraphParam); //Наполняем параграф
BSTR str = SysAllocString(L"Первая строка");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); str = SysAllocString(L"Вторая строка");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); //Завершаем параграф
Document2D->ksEndObj(); paragraphParam.Unbind();
textItemParam.Unbind();

Как всегда, здесь для простоты опущен код, ответственный за создание и оформление документа (эта тема рассматривалась на прошлых уроках).

На рисунке ниже показан сформированный параграф. В данном примере КОМПАС сам определяет размер параграфа на основе его содержимого.

Мы не указали ширину параграфа, поэтому КОМПАС автоматически увеличивает ее по мере необходимости. Обратите внимание: текст выводится как одна строка. Если бы ширина была задана, то поведение КОМПАС определялось бы значением свойства hFormat интерфейса ksParagraphParam.

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

Пример. Многострочный текст

Для явного переноса на новую строку используется флаг NEW_LINE (0x1000).

Ниже приводится пример программы, демонстрирующий построение многострочного параграфа с помощью этого флага.

//Получаем интерфейс ksTextItemParam
TextItemParamPtr textItemParam;
textItemParam = static_cast<TextItemParamPtr>(kompas->GetParamStruct(ko_TextItemParam)); //Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast<ParagraphParamPtr>(kompas->GetParamStruct(ko_ParagraphParam)); paragraphParam->Init();
paragraphParam->set_x(100.0);
paragraphParam->set_y(100.0); //Начинаем параграф
Document2D->ksParagraph(paragraphParam); //Наполняем параграф
BSTR str = SysAllocString(L"Первая строка");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); TextItemFontPtr textItemFont;
textItemFont = static_cast<TextItemFontPtr>(textItemParam->GetItemFont());
textItemFont->SetBitVectorValue(NEW_LINE, true);
str = SysAllocString(L"Вторая строка");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str);
textItemFont.Unbind(); str = SysAllocString(L"Третья строка");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); //Завершаем параграф
Document2D->ksEndObj(); paragraphParam.Unbind();
textItemParam.Unbind();

В данном примере создается параграф из трёх строк. Первая строка выводится как обычно. Для второй устанавливается флаг NEW_LINE. Он говорит о начале новой строки. Третья выводится как обычно, но для нее по-прежнему действует флаг NEW_LINE, так как мы работаем с тем же экземпляром интерфейса ksTextItemParam. На рисунке ниже показан параграф, сформированный этой программой.


Теперь строки выводятся правильно.

Выравнивание текста

Выравнивание текста задается методом ksSetTextLineAlign интерфейса ksDocument2D. У него всего один целочисленный параметр – устанавливаемое выравнивание. Его допустимые значения перечислены в таблице ниже.

В случае успеха метод ksSetTextLineAlign возвращает предыдущий признак выравнивания, а в случае ошибки – число -1.

Это значит, что с его помощью нельзя задавать выравнивание для текста, выводимого методом ksText. Учтите, что метод ksSetTextLineAlign может использоваться только внутри блока (в нашем случае он используется внутри параграфа). Рассмотрим пример (здесь используется сильно упрощенный синтаксис по сравнению с их оригиналами): Данное ограничение связано с тем, что в этом случае КОМПАС не знает, относительно каких границ нужно выравнивать текст.
Другой важный момент связан с областью действия метода ksSetTextLineAlign – на какие выводимые строки он действует.

ksSetTextLineAlign(1);
ksTextLine(“По центру”); ksSetTextLineAlign(2);
ksTextLine(“По правому краю”);

Как будут выровнены строки? Вопреки нашим ожиданиям обе строки будут выровнены по правому краю. Почему? Дело в том, что метод ksSetTextLineAlign в первую очередь изменяет выравнивание последней выведенной строки. Вот что происходит в нашем примере: в первой строке устанавливается выравнивание по центру. Поскольку предыдущей выводимой строки нет, данный вызов меняет выравнивание по умолчанию (по левому краю).

Изначально для нее используется выравнивание по центру, установленное ранее. Затем мы выводим строку «По центру».

В первую очередь метод изменяет выравнивание предыдущей строки («По центру»). В третьей строке мы вновь меняем выравнивание. Это же выравнивание становится выравниваем по умолчанию.
Мы выводим строку «По правому краю». Поэтому она выравнивается по правому краю, а не по центру, как мы планировали. Теперь немного изменим пример: Поскольку метод ksSetTextLineAlign больше не вызывается, то для нее используется ранее установленное выравнивание (по правому краю).
Таким образом, обе строки выравниваются по правому краю.

ksSetTextLineAlign(1);
ksTextLine(“По центру”); ksTextLine(“”); ksSetTextLineAlign(2);
ksTextLine(“По правому краю”);

Все что мы изменили – добавили вывод пустой строки без изменения выравнивания. Теперь строки выводятся правильно. Это происходит, потому что вывод пустой строки «поглощает» устанавливаемое выравнивание по правому краю. Второй вызов метода ksSetTextLineAlign влияет на пустую строку и никак не влияет на строку «По центру».
Пример ниже показывает правильное выравнивание без вывода пустой строки.

ksTextLine(“По центру”);
ksSetTextLineAlign(1); ksTextLine(“По правому краю”);
ksSetTextLineAlign(2);

Вызовы ksTextLine и ksSetTextLineAlign поменяны местами. Поскольку метод ksSetTextLineAlign в первую очередь влияет на последнюю выведенную строку, то выравнивания устанавливаются правильно, и строки выводятся так, как мы того и хотели.

Пример

Ниже приводится исходный текст программы, демонстрирующей выравнивание текста в параграфе.

//Получаем интерфейс ksTextItemParam
TextItemParamPtr textItemParam;
textItemParam = static_cast<TextItemParamPtr>(kompas->GetParamStruct(ko_TextItemParam)); //Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast<ParagraphParamPtr>(kompas->GetParamStruct(ko_ParagraphParam)); paragraphParam->Init();
paragraphParam->set_x(100.0);
paragraphParam->set_y(100.0);
paragraphParam->set_width(60.0);
paragraphParam->set_hFormat(2); //Начинаем параграф
Document2D->ksParagraph(paragraphParam); //Наполняем параграф
BSTR str = SysAllocString(L"Текст выровненный по левому краю");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); TextItemFontPtr textItemFont;
textItemFont = static_cast<TextItemFontPtr>(textItemParam->GetItemFont());
textItemFont->SetBitVectorValue(NEW_LINE, true);
str = SysAllocString(L"");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str);
textItemFont.Unbind(); str = SysAllocString(L"По центру");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str);
Document2D->ksSetTextLineAlign(1); str = SysAllocString(L"");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); str = SysAllocString(L"Длинный текст выравниваемый по ширине");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str);
Document2D->ksSetTextLineAlign(3); //Завершаем параграф
Document2D->ksEndObj(); paragraphParam.Unbind();
textItemParam.Unbind();

В данном примере помимо выравнивания текста также демонстрируется использование свойств width и hFormat интерфейса ksParagraphParam. Они используются для ограничения его ширины. Если бы мы их не изменили, КОМПАС увеличил бы ширину параграфа, и мы не увидели бы выравнивания по левому краю и по ширине.

Они никак не влияют на правильность выравнивания. Пустые строки выводятся для улучшения читаемости параграфа.

На рисунке ниже показан параграф, сформированный данной программой.

Включение и выключение начертания

На 11 уроке цикла мы рассматривали флаги, управляющие начертанием (ITALIC_ON, ITALIC_OFF, BOLD_ON, UNDERLINE_ON и UNDERLINE_OFF). Тогда мы рассматривали их применительно к методу ksText. Важное отличие их применения в параграфе заключается в том, что действие не ограничивается вызовом метода ksTextLine, а простирается на весь параграф. Рассмотрим несколько примеров.

TextItemFont->SetBitVectorValue(BOLD_ON, true);
TextItemParam->s = SysAllocString(L”Первая строка”);
Document2D->ksTextLine(TextItemParam); TextItemFont->Init();
TextItemParam->s = SysAllocString(L”Вторая строка”);
Document2D->ksTextLine(TextItemParam);

Первая строка будет выведена полужирным шрифтом. С этим вопросов не возникает. Но как будет выведена вторая строка? Для нее флаг BOLD_ON сброшен. Поэтому можно предположить, что она будет выведена обычным шрифтом. Но это не так. Встретив флаг BOLD_ON, КОМПАС понимает команду так: все последующие строки данного параграфа выводятся полужирным шрифтом. Поэтому все последующие строки выводятся полужирным шрифтом до тех пор, пока параграф не будет завершен или КОМПАС не встретит парный ему флаг BOLD_OFF. Рассмотрим пример:

TextItemFont.SetBitVectorValue(BOLD_ON, true);
TextItemParam.s = SysAllocString(L”Первая строка”);
Document2D.ksTextLine(TextItemParam); TextItemFont.Init();
TextItemFont.SetBitVectorValue(BOLD_OFF, true);
TextItemParam.s = SysAllocString(L“Вторая строка”);
Document2D.ksTextLine(TextItemParam); TextItemFont.Init();
TextItemParam.s = SysAllocString(L”Третья строка”);
Document2D.ksTextLine(TextItemParam);

Первая строка выводится полужирным шрифтом. Для второй строки мы сбрасываем флаг BOLD_ON и взводим парный ему флаг BOLD_OFF, который отменяет полужирное начертание. Благодаря этому вторая и третья строки выводятся без полужирного начертания.

Такое поведение распространяется на флаги ITALIC_ON, ITALIC_OFF, UNDERLINE_ON и UNDERLINE_OFF, но не распространяется на флаг NEW_LINE, так как для него нет парного отменяющего флага.

Пример

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

//Получаем интерфейс ksTextItemParam
TextItemParamPtr textItemParam;
textItemParam = static_cast<TextItemParamPtr>(kompas->GetParamStruct(ko_TextItemParam)); //Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast<ParagraphParamPtr>(kompas->GetParamStruct(ko_ParagraphParam)); paragraphParam->Init();
paragraphParam->set_x(100.0);
paragraphParam->set_y(100.0); //Начинаем параграф
Document2D->ksParagraph(paragraphParam); //Наполняем параграф
BSTR str = SysAllocString(L"Обычный текст");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); TextItemFontPtr textItemFont;
textItemFont = static_cast<TextItemFontPtr>(textItemParam->GetItemFont());
textItemFont->set_bitVector(NEW_LINE | ITALIC_OFF); // str = SysAllocString(L"Текст без наклона");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); textItemFont->set_bitVector(NEW_LINE | ITALIC_ON | BOLD_ON); //
str = SysAllocString(L"Полужирный текст");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); textItemFont->set_bitVector(NEW_LINE | BOLD_OFF | UNDERLINE_ON); //
str = SysAllocString(L"Подчеркнутый текст");
textItemParam->set_s(str);
Document2D->ksTextLine(textItemParam);
SysFreeString(str); //Завершаем параграф
Document2D->ksEndObj(); paragraphParam.Unbind();
textItemFont.Unbind();
textItemParam.Unbind();

Самая важная часть данной программы – правильная установка флагов для выводимых строк. Разберем ее более подробно (соответствующие строки программы помечены парой символов «//»).

Поэтому для нее не устанавливаются никакие флаги. Первая строка выводится без каких-либо изменений.

Поэтому для нее устанавливаются флаги: NEW_LINE (начинать с новой строки) и ITALIC_OFF (отключить курсивное начертание). Вторая строка должна выводиться без наклона и с новой строчки.

Для этого мы взводим флаги: NEW_LINE, ITALIC_ON (включаем курсив) и BOLD_ON (включаем полужирное начертание). Третья строка должна выводиться курсивом и полужирным шрифтом. Все остальные флаги сбрасываем.

Для этого мы взводим флаги: NEW_LINE, BOLD_OFF (отключить полужирное начертание, оставшееся включенным от предыдущей строки) и UNDERLINE_ON (включить подчеркнутое начертание). Четвертая строка должна быть выведена курсивом, подчеркнутым и не полужирным начертанием.

Для отключения подчеркнутого начертания нужно сбросить флаг UNDERLINE_ON и взвести флаг UNDERLINE_OFF. Если бы в параграфе были еще строки, то они выводились бы курсивным подчеркнутым шрифтом.

На рисунке ниже показан результат работы этой программы.

Отделение информации от представления

Если вы следите за структурой своих программ, то наверняка заметили серьезный недостаток предыдущего примера: код, ответственный за формирование выводимой информации, перемешан с кодом, отвечающим за реализацию ее вывода. При хорошем стиле программирования принято отделять информацию от ее представления.

Метод ksTextLine умеет обрабатывать оба этих интерфейса. Если выводимая информация состоит из нескольких строк ksTextItemParam, то их можно объединить в один интерфейс ksTextLineParam. То есть вся информация будет выведена в одну строку, даже если для некоторых экземпляров ksTextItemParam взведен флаг NEW_LINE. Но у такого подхода есть неприятное ограничение: если метод ksTextLine принимает интерфейс ksTextLineParam, то флаги NEW_LINESPECIAL_SYMBOL_END) игнорируются. Для обхода этого ограничения придется вручную вызывать ksTextLine для каждой строки.

Ниже приводится исходный код примера, демонстрирующего эту технику.

//Получаем интерфейс массива строк
DynamicArrayPtr dynamicArray;
dynamicArray = static_cast<DynamicArrayPtr>(kompas->GetDynamicArray(TEXT_ITEM_ARR));
dynamicArray->ksClearArray(); //Получаем интерфейс ksTextItemParam
TextItemParamPtr textItemParam;
textItemParam = static_cast<TextItemParamPtr>(kompas->GetParamStruct(ko_TextItemParam)); //Наполняем массив строк
BSTR str = SysAllocString(L"Обычный текст");
textItemParam->set_s(str);
dynamicArray->ksAddArrayItem(-1, textItemParam);
SysFreeString(str); TextItemFontPtr textItemFont;
textItemFont = static_cast<TextItemFontPtr>(textItemParam->GetItemFont());
textItemFont->set_bitVector(NEW_LINE | ITALIC_OFF); str = SysAllocString(L"Текст без наклона");
textItemParam->set_s(str);
dynamicArray->ksAddArrayItem(-1, textItemParam);
SysFreeString(str); textItemFont->set_bitVector(NEW_LINE | ITALIC_ON | BOLD_ON);
str = SysAllocString(L"Полужирный текст");
textItemParam->set_s(str);
dynamicArray->ksAddArrayItem(-1, textItemParam);
SysFreeString(str); textItemFont->set_bitVector(NEW_LINE | BOLD_OFF | UNDERLINE_ON);
str = SysAllocString(L"Подчеркнутый текст");
textItemParam->set_s(str);
dynamicArray->ksAddArrayItem(-1, textItemParam);
SysFreeString(str); //Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast<ParagraphParamPtr>(kompas->GetParamStruct(ko_ParagraphParam)); paragraphParam->Init();
paragraphParam->set_x(100.0);
paragraphParam->set_y(100.0); //Начинаем параграф
Document2D->ksParagraph(paragraphParam); //Выводим в параграф содержимое массива
for(unsigned int i = 0; i < dynamicArray->ksGetArrayCount(); ++i)
{ dynamicArray->ksGetArrayItem(i, textItemParam); Document2D->ksTextLine(textItemParam);
} //Завершаем параграф
Document2D->ksEndObj(); //Освобождаем интерфейсы
textItemFont.Unbind();
textItemParam.Unbind();
paragraphParam.Unbind();
dynamicArray->ksDeleteArray();
dynamicArray.Unbind();

В этом примере выводимые строки сначала заносятся в динамический массив DynamicArray, а уже потом выводятся в параграф. Это позволяет отделить информацию от ее представления. Если бы в нашем примере не использовался флаг NEW_LINE, то мы могли бы обойтись одним вызовом метода ksTextLine.

Результат работы этой программы аналогичен результату работы предыдущего примера.

Заключение

Также мы научились отделять информацию от ее представления. На данном уроке мы рассмотрели, как строить параграф и как с помощью него выводить многострочный текст. Это не очень удобно. К сожалению, корректный вывод многострочного текста требует ручного обхода массива строк. На следующем уроке я покажу, как решить эту проблему.

Продолжение следует, следите за новостями блога.

Сергей Норсеев, к.т.н., автор книги «Разработка приложений под КОМПАС в Delphi».


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Изучаем Go: подборка видеозаписей докладов

Мы собрали для вас подборку видео, в основном с наших митапов, в которых разбираются преимущества и недостатки языка, и рассказывается об особенностях его применения на примерах реальных проектов. Сегодня язык Go широко используется для разработки распределённых и высоконагруженных приложений. Список ...

[Перевод] Особенности использования типа данных Symbol в JavaScript

Символьные примитивы — это одно из новшеств стандарта ES6, которое принесло в JavaScript некоторые ценные возможности. Символы, представленные типом данных Symbol, особенно полезны при использовании их в качестве идентификаторов свойств объектов. В связи с таким сценарием их применения напрашивается вопрос ...