Работа с API КОМПАС-3D → Урок 13 → Параграфы
Прежде чем перейти к рассмотрению документированных способов создания составных строк, нам нужно познакомиться с таким объектом, как параграф. Он представляет собой автоматически форматируемый блок текста, состоящий из нескольких строк. На данном уроке мы рассмотрим вопросы построения простых параграфов.
Содержание цикла уроков «Работа с API КОМПАС-3D»
- Основы
- Оформление чертежа
- Корректное подключение к КОМПАС
- Основная надпись
- Графические примитивы
- Сохранение документа в различные форматы
- Знакомство с настройками
- Более сложные методы записи в основную надпись
- Чтение ячеек основной надписи
- Спецсимволы, включающие строку
- Простые текстовые надписи
- Составные строки
- Параграфы
- Многострочный текст
Параметры параграфа (ksParagraphParam)
Параграф описывается интерфейсом ksParagraphParam. Для его получения нужно использовать метод GetParamStruct интерфейса KompasObject, для этого ему нужно передать константу ko_ParagraphParam (0×0000001B). Рассмотрим свойства интерфейса ksParagraphParam.
ang — угол наклона текста в градусах. Откладывается от горизонтальной линии против часовой стрелки. Аналогичен параметру ang метода ksText.
height — высота параграфа в миллиметрах.
hFormat — форматирование текста по горизонтали. Данное свойство используется, когда текст не умещается в параграф по ширине. Допустимые значения перечислены в таблице ниже.
style — стиль текста (описывался на уроке 11).
vFormat — форматирование текста по вертикали. Данное свойство используется, когда текст не умещается в параграф по высоте. Допустимые значения перечислены в таблице ниже.
При работе со свойством vFormat нужно помнить два момента:
- Согласно документации КОМПАС допустимыми значениями свойства vFormat являются значения 0 и -1, но это не так. Допустимые значения: 0 и 1.
- КОМПАС не меняет высоту символов. Он изменяет только расстояние между строками. Если высота строк меньше высоты параграфа, то они могут накладываться друг на друга. Пример такого наложения — на рисунке ниже.
width — ширина параграфа в миллиметрах.
Свойства height, hFormat, vFormat и width позволяют решать задачу размещения текста в заданном прямоугольнике. Этот метод гораздо надежнее и эффективнее в отличие от метода ksGetTextLength, обсуждавшегося на уроке 11.
x и y — координаты точки привязки. Положение параграфа относительно точки привязки вдоль горизонтальной оси настраивается методом ksSetTextAlign интерфейса ksDocument2D (правда, такая возможность не документирована). По вертикали точка привязки всегда совпадает с низом первой строки параграфа. Изменить это поведение нельзя.
Метод у интерфейса ksParagraphParam всего один: Init (). Он инициализирует значения свойств интерфейса. Не имеет входных параметров. В случае успеха возвращает значение true.
Построение параграфа
Создание параграфа состоит из трёх последовательных этапов.
- Объявление начала параграфа. Для этого вызывается метод ksParagraph интерфейса ksDocument2D. В качестве единственного параметра данный метод принимает интерфейс ksParagraphParam, задающий параметры параграфа. В случае успеха метод ksParagraph возвращает единицу, а в случае ошибки — ноль.
- Наполнение параграфа. Для каждой строки, выводимой в параграф, вызывается метод ksTextLine интерфейса ksDocument2D. В качестве единственного параметра он принимает интерфейс ksTextItemParam или ksTextLineParam (рассматривались на предыдущих уроках цикла), описывающие строку. Учтите, выводимые строки не должны содержать символы @, $, &, ~, ^ и #, так как они являются управляющими символами. Работа с ними будет рассмотрена на следующих уроках цикла.
- Завершение параграфа. Для этого вызывается метод ksEndObj () интерфейса ksDocument2D. Он не имеет входных параметров и в случае успеха возвращает целочисленный указатель на созданный объект (параграф). В случае ошибки он возвращает ноль.
Пример. Простейший параграф
//Получаем интерфейс ksTextItemParam
TextItemParamPtr textItemParam;
textItemParam = static_cast(kompas->GetParamStruct(ko_TextItemParam));
//Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast(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 (0×1000).
//Получаем интерфейс ksTextItemParam
TextItemParamPtr textItemParam;
textItemParam = static_cast(kompas->GetParamStruct(ko_TextItemParam));
//Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast(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(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.
Учтите, что метод ksSetTextLineAlign может использоваться только внутри блока (в нашем случае он используется внутри параграфа). Это значит, что с его помощью нельзя задавать выравнивание для текста, выводимого методом ksText. Данное ограничение связано с тем, что в этом случае КОМПАС не знает, относительно каких границ нужно выравнивать текст.
Другой важный момент связан с областью действия метода 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(kompas->GetParamStruct(ko_TextItemParam));
//Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast(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(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(kompas->GetParamStruct(ko_TextItemParam));
//Подготавливаем интерфейс параметров параграфа
ParagraphParamPtr paragraphParam;
paragraphParam = static_cast(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(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.
На рисунке ниже показан результат работы этой программы.
Отделение информации от представления
Если вы следите за структурой своих программ, то наверняка заметили серьезный недостаток предыдущего примера: код, ответственный за формирование выводимой информации, перемешан с кодом, отвечающим за реализацию ее вывода. При хорошем стиле программирования принято отделять информацию от ее представления.
Если выводимая информация состоит из нескольких строк ksTextItemParam, то их можно объединить в один интерфейс ksTextLineParam. Метод ksTextLine умеет обрабатывать оба этих интерфейса. Но у такого подхода есть неприятное ограничение: если метод ksTextLine принимает интерфейс ksTextLineParam, то флаги NEW_LINE (и SPECIAL_SYMBOL_END) игнорируются. То есть вся информация будет выведена в одну строку, даже если для некоторых экземпляров ksTextItemParam взведен флаг NEW_LINE. Для обхода этого ограничения придется вручную вызывать ksTextLine для каждой строки.
//Получаем интерфейс массива строк
DynamicArrayPtr dynamicArray;
dynamicArray = static_cast(kompas->GetDynamicArray(TEXT_ITEM_ARR));
dynamicArray->ksClearArray();
//Получаем интерфейс ksTextItemParam
TextItemParamPtr textItemParam;
textItemParam = static_cast(kompas->GetParamStruct(ko_TextItemParam));
//Наполняем массив строк
BSTR str = SysAllocString(L"Обычный текст");
textItemParam->set_s(str);
dynamicArray->ksAddArrayItem(-1, textItemParam);
SysFreeString(str);
TextItemFontPtr textItemFont;
textItemFont = static_cast(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(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».