Если не хочется особенно разбираться, то в 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, т.к. пословный перенос особенно и не интересен, а вот посимвольный вывод ускорился в разы. |