Модераторы: feodorv, GremlinProg, xvr, Fixin
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Перенос такста по символам, а не по словам 
:(
    Опции темы
586
Дата 1.5.2008, 00:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2243
Регистрация: 8.5.2006

Репутация: 39
Всего: 146



Как в MessageBox реализован перенос текста? Мне нужен перенос не только по словам, но и по символам, для длинных строк.
Код
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    static const char text[] = "aaaaaa\r\n"
                               "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\n"
                               "cccccc\r\n"
                               "dddd eeeeeee ffffffffff ggggggggg hhhh\r\n"
                               "iiiiiiii";
    RECT rc = {5, 5, 100, 150};
    DrawText(Canvas->Handle, text, strlen(text), &rc, DT_WORDBREAK);
}

Вторая строка не переносится.
PM   Вверх
GremlinProg
Дата 3.5.2008, 12:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Если не хочется особенно разбираться, то в RichEdit есть возможность установить пользовательскую функцию переноса слов(EM_SETWORDBREAKPROC), а чтобы вывести этот текст в другой девайс, используй EM_FORMATRANGE.

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

Я сделал свой вариант печати, тоже потребовался вывод с нормальным переносом, но в моем случае нужна был еще и подсветка синтаксиса.  Я реализовал класс rtf_printer так:
Код

class rtf_printer{
protected:
    RECT        rc;
    SIZE        size;
    int            x,y;
    int            line;
    int            height;
    //
    BYTE        print_state    : 1;
    BYTE        calc_size    : 1;
    void inc_x(void){
        x        += size.cx;
        line    -= size.cx;
        if(max_width < x){
            max_width    = x;
        }
    }
    void inc_y(void){
        y    += height;
    }
    void start_line(void){
        x    = rc.left;
    }
    void next_line(void){
        start_line();
        inc_y();
        if(max_height < y){
            max_height    = y;
        }
    }
    bool last_line(void){
        return( (rc.bottom - y) < 2 * height );
    }
    // print_text вернет число распечатанных символов
    int print_text(HDC hdc,LPCWSTR lpszWord,int len){
        SIZE extra;
        if(!len)return len;
        // получение первоначальной длины текста в пикселах, используя текущий шрифт
        ::GetTextExtentPoint32W(hdc,lpszWord,len,&size);
        // если сегмент не последний, extra.cx = 0
        extra.cx    = 0;
        if(last_line()){// последняя линия
            if(x + line > rc.right){//остаток не вмещается на линию
                // получение длины символа "…" в пикселах, используя текущий шрифт
                ::GetTextExtentPoint32W(hdc,L"…",1,&extra);
            }
        }
        // поиск оптимальной длины текста, который вмещается на линию (с учетом многоточия, если нужно)
        while(len && (x + size.cx > rc.right - extra.cx)){
            ::GetTextExtentPoint32W(hdc,lpszWord,--len,&size);
        }
        // вывод расчитанного сегмента текста
        if(!calc_size){
            print_segment(hdc,lpszWord,len);
        }
        // продвижение координаты x
        inc_x();
        //
        if(!len){// текст не поместился в линию, т.е. ни один символ не может быть на ней распечатан
            if(last_line()){// последняя линия
                //если многоточие помещается на линию
                if(x + extra.cx <= rc.right){
                    // и поднят флаг печати
                    if(!calc_size){
                        // вывести многоточие
                        print_segment(hdc,L"…",1);
                    }
                    // это только для корректной обработки в inc_x
                    size    = extra;
                    // здесь inc_x - только для учета многоточия в max_width
                    inc_x();
                }
            }
            // начать новую линию (продвижение координат)
            next_line();
        }
        return len;
    }
protected:
    virtual int prepare_dc(HDC hdc,LPCWSTR lpszText,int offset,int length){
        return length;
    }
    virtual void restore_dc(HDC hdc){
    }
    virtual bool print_segment(HDC hdc,LPCWSTR lpszSegment,int length){
        return(::TextOutW(hdc,x,y,lpszSegment,length) != FALSE);
    }
public:
    int            max_width;
    int            max_height;
    // print вернет число не распечатанных символов
    int print(HDC hdc,LPCWSTR lpszText,int length,CONST RECT&rc,bool calc_size){
        TEXTMETRIC tm;
        max_width        = rc.left;
        y                = rc.top;
        height            = 0;
        line            = 0;
        print_state        = false;
        this->calc_size    = calc_size;
        if(!::CopyRect(&this->rc,&rc))return length;
        UINT align;
        int len,calc_length,offset;
        align        = ::SetTextAlign(hdc,TA_LEFT|TA_TOP);
        //вычисление высоты строки
        ::GetTextMetrics(hdc,&tm);
        height        = tm.tmHeight + tm.tmExternalLeading + (tm.tmUnderlined?1:0);
        //
        for(offset = 0,calc_length = length;calc_length;calc_length -= len,offset += len){
            //перевод устройства в пользовательское состояние
            len    = prepare_dc(hdc,lpszText,offset,calc_length);
            //проверка корректности пользовательского ввода
            assert((len > 0) && (len <= calc_length));
            //вычисление размера текста
            ::GetTextExtentPoint32W(hdc,lpszText + offset,len,&size);
            //вычисление общей длины текста
            line    += size.cx;
            //вычисление общей высоты текста
            ::GetTextMetrics(hdc,&tm);
            if(height < tm.tmHeight + tm.tmExternalLeading + (tm.tmUnderlined?1:0)){
                height    = tm.tmHeight + tm.tmExternalLeading + (tm.tmUnderlined?1:0);
            }
            //восстановление устройства в состояние "по-умолчанию"
            restore_dc(hdc);
        }
        print_state        = true;
        start_line();
        for(offset = 0;(length) && (y + height <= rc.bottom);length -= len,offset += len){
            //перевод устройства в пользовательское состояние
            len    = prepare_dc(hdc,lpszText,offset,length);
            //проверка корректности пользовательского ввода
            assert((len > 0) && (len <= length));
            //вывод текста
            len    = print_text(hdc,lpszText + offset,len);
            //восстановление устройства в состояние "по-умолчанию"
            restore_dc(hdc);
        }
        ::SetTextAlign(hdc,align);
        max_width    -= rc.left;
        max_height     = y - rc.top;
        if(y + height <= rc.bottom){
            max_height+=height;
        }
        return length;
    }
};

код для юникода, кому нужно, локализуют как надо.
пример:
Код

rtf_printer p;
p.print(hdc,lpszText,(int)wcslen(lpszText),rc,false);

здесь rc - прямоугольник в координатах hdc, в котором нужно вывести текст lpszText.
Алгоритм пытается распределить текст по ширине прямоугольника, если солово длинное - переносит его на новую строку, если текст не помещается в прямоугольнике - добавляет в конец многоточие.

prepare_dc и restore_dc - виртуальные методы для раскраски отдельных частей текста.
prepare_dc должен вернуть длину разобранного слова, если оно меньше length. По умолчанию, она возвращает length.

параметр calc_size позволяет расчитать точные ширину и высоту в прямоугольнике, т.е. не выводит текст, а только считает его. Результаты на выходе в параметрах max_width и max_height, т.е. можно перед выводом отцентрировать многострочный текст как надо.

Если текст простой, сплошной, не rtf, то можно убрать первый цикл в основном методе print, он считает ширину и высоту всего текста, как если бы он был распечатан без переносов.
Код

for(offset = 0,calc_length = length;calc_length;)...


Вообще тут можно дополнительно к раскраске обрабатывать hittest на отдельных элементах скрипта, поэтому вывод отдельной последовательности print, сделан виртуальным.(в нем можно использовать x,y - координаты слова, а size - его ширина и высота)

Один момент, который пришлось подгонять вручную:  при расчете шрифта с подчеркиванием ни как не учитывается само подчеркивание, поэтому я тут просто добавляю единицу к высоте текста.

Высота текста для любого форматирования - всегда доинакова на всех линиях, поэтому нежелательно использовать несколько разновысотных шрифтов, т.к. вывод не с базовой линии, а с левого-верхнего угла.

PS: упростил класс rtf_printer, т.к. пословный перенос особенно и не интересен, а вот посимвольный вывод ускорился в разы.

Это сообщение отредактировал(а) GremlinProg - 18.5.2008, 23:24


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.0705 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.